Skip to content

Perl 文件操作

打开和关闭文件

打开文件读取

perl
open(my $fh, '<', 'input.txt') or die "Cannot open file: $!";

while (my $line = <$fh>) {
    print $line;
}

close($fh);

打开文件写入

perl
open(my $fh, '>', 'output.txt') or die "Cannot open file: $!";

print $fh "Hello, World!\n";
print $fh "This is a test.\n";

close($fh);

打开文件追加

perl
open(my $fh, '>>', 'log.txt') or die "Cannot open file: $!";

print $fh "Log entry: " . localtime() . "\n";

close($fh);

打开模式总结

模式说明如果文件存在如果文件不存在
<读取从开头读取报错
>写入清空并写入创建新文件
>>追加在末尾追加创建新文件
+<读写从开头读写报错
+>读写清空并读写创建新文件
+>>读写追加读写创建新文件

读取文件

逐行读取

perl
open(my $fh, '<', 'data.txt') or die "Cannot open file: $!";

while (my $line = <$fh>) {
    chomp $line;  # 移除换行符
    print "$line\n";
}

close($fh);

读取整个文件到数组

perl
open(my $fh, '<', 'data.txt') or die "Cannot open file: $!";

my @lines = <$fh>;  # 读取所有行
close($fh);

foreach my $line (@lines) {
    chomp $line;
    print "$line\n";
}

读取整个文件到标量

perl
open(my $fh, '<', 'data.txt') or die "Cannot open file: $!";

# 方法 1:使用 local
local $/;
my $content = <$fh>;
close($fh);

print $content;

# 方法 2:使用 File::Slurp
use File::Slurp;
my $content2 = read_file('data.txt');

读取固定字节数

perl
open(my $fh, '<', 'data.txt') or die "Cannot open file: $!";

my $buffer;
my $bytes_read = read($fh, $buffer, 1024);  # 读取 1024 字节

print "Read $bytes_read bytes\n";
print $buffer;

close($fh);

使用 sysread 和 syswrite

perl
open(my $fh, '<', 'data.txt') or die "Cannot open file: $!";

my $buffer;
my $bytes_read = sysread($fh, $buffer, 256);

print "Read $bytes_read bytes using sysread\n";

close($fh);

写入文件

写入文本

perl
open(my $fh, '>', 'output.txt') or die "Cannot open file: $!";

print $fh "Line 1\n";
print $fh "Line 2\n";
print $fh "Line 3\n";

close($fh);

使用 say 自动换行(Perl 5.10+)

perl
use v5.10;

open(my $fh, '>', 'output.txt') or die "Cannot open file: $!";

say $fh "Line 1";
say $fh "Line 2";
say $fh "Line 3";

close($fh);

格式化写入

perl
open(my $fh, '>', 'report.txt') or die "Cannot open file: $!";

printf $fh "%-20s %10s %10s\n", "Name", "Age", "Score";
printf $fh "%-20s %10d %10d\n", "Alice", 25, 95;
printf $fh "%-20s %10d %10d\n", "Bob", 30, 88;

close($fh);

使用 syswrite

perl
open(my $fh, '>', 'output.txt') or die "Cannot open file: $!";

my $data = "Hello, World!";
my $bytes_written = syswrite($fh, $data);

print "Wrote $bytes_written bytes\n";

close($fh);

追加写入

perl
open(my $fh, '>>', 'log.txt') or die "Cannot open file: $!";

my $timestamp = localtime();
print $fh "[$timestamp] Log message\n";

close($fh);

文件测试

基本文件测试

perl
my $file = 'test.txt';

if (-e $file) {
    print "File exists\n";
}

if (-f $file) {
    print "Is a regular file\n";
}

if (-d $file) {
    print "Is a directory\n";
}

if (-r $file) {
    print "Is readable\n";
}

if (-w $file) {
    print "Is writable\n";
}

if (-x $file) {
    print "Is executable\n";
}

文件大小和时间

perl
my $file = 'test.txt';

my $size = -s $file;
print "File size: $size bytes\n";

my $mtime = -M $file;
print "Modified $mtime days ago\n";

my $atime = -A $file;
print "Accessed $atime days ago\n";

my $ctime = -C $file;
print "Changed $ctime days ago\n";

文件权限

perl
my $file = 'test.txt';

printf "Owner: %04o\n", (stat($file))[2] & 0777;

if (-r $file && -w $file) {
    print "Readable and writable\n";
}

if (-R $file) {
    print "Real user can read\n";
}

if (-W $file) {
    print "Real user can write\n";
}

文件类型

perl
my $file = 'test.txt';

if (-f $file) { print "Regular file\n"; }
if (-d $file) { print "Directory\n"; }
if (-l $file) { print "Symbolic link\n"; }
if (-p $file) { print "Named pipe\n"; }
if (-S $file) { print "Socket\n"; }
if (-b $file) { print "Block special file\n"; }
if (-c $file) { print "Character special file\n"; }

文件和目录操作

删除文件

perl
# 删除单个文件
unlink 'file.txt' or warn "Cannot delete file: $!";

# 删除多个文件
my @files = ('file1.txt', 'file2.txt', 'file3.txt');
my $count = unlink @files;
print "Deleted $count files\n";

重命名文件

perl
rename('old.txt', 'new.txt') or die "Cannot rename: $!";

复制文件

perl
# 方法 1:使用 File::Copy
use File::Copy;

copy('source.txt', 'destination.txt') or die "Cannot copy: $!";

# 方法 2:手动复制
open(my $in, '<', 'source.txt') or die "Cannot open source: $!";
open(my $out, '>', 'destination.txt') or die "Cannot open destination: $!";

while (my $line = <$in>) {
    print $out $line;
}

close($in);
close($out);

移动文件

perl
# 使用 File::Copy
use File::Copy;

move('source.txt', 'destination.txt') or die "Cannot move: $!";

# 或使用 rename
rename('source.txt', 'destination.txt') or die "Cannot move: $!";

获取文件信息

perl
my $file = 'test.txt';

my @stats = stat($file);

my $dev = $stats[0];   # 设备号
my $ino = $stats[1];   # inode 号
my $mode = $stats[2];  # 文件模式
my $nlink = $stats[3]; # 硬链接数
my $uid = $stats[4];   # 用户 ID
my $gid = $stats[5];   # 组 ID
my $rdev = $stats[6];  # 特殊设备号
my $size = $stats[7];  # 文件大小
my $atime = $stats[8]; # 访问时间
my $mtime = $stats[9]; # 修改时间
my $ctime = $stats[10]; # 改变时间
my $blksize = $stats[11]; # 块大小
my $blocks = $stats[12]; # 块数量

printf "File: $file\n";
printf "Size: %d bytes\n", $size;
printf "Modified: %s\n", scalar localtime($mtime);
printf "Permissions: %04o\n", $mode & 0777;

二进制文件操作

读取二进制文件

perl
open(my $fh, '<:raw', 'binary.dat') or die "Cannot open file: $!";

binmode($fh);

my $buffer;
my $bytes_read = read($fh, $buffer, 1024);

print "Read $bytes_read bytes\n";

close($fh);

写入二进制文件

perl
open(my $fh, '>:raw', 'output.dat') or die "Cannot open file: $!";

binmode($fh);

my $data = pack('C*', 0x01, 0x02, 0x03, 0x04);
print $fh $data;

close($fh);

使用 pack 和 unpack

perl
# 写入二进制数据
open(my $fh, '>:raw', 'data.dat') or die "Cannot open file: $!";

my $header = pack('A4LL', 'TEST', 12345, 67890);
print $fh $header;

close($fh);

# 读取二进制数据
open(my $fh, '<:raw', 'data.dat') or die "Cannot open file: $!";

my $buffer;
read($fh, $buffer, 12);  # 读取 12 字节

my ($magic, $num1, $num2) = unpack('A4LL', $buffer);

print "Magic: $magic\n";
print "Number 1: $num1\n";
print "Number 2: $num2\n";

close($fh);

实践示例

示例 1:文件统计工具

perl
#!/usr/bin/perl
use strict;
use warnings;

sub count_file_stats {
    my ($filename) = @_;
    
    open(my $fh, '<', $filename) or die "Cannot open $filename: $!";
    
    my $line_count = 0;
    my $word_count = 0;
    my $char_count = 0;
    
    while (my $line = <$fh>) {
        $line_count++;
        $char_count += length($line);
        
        my @words = split /\s+/, $line;
        $word_count += scalar @words;
    }
    
    close($fh);
    
    return {
        lines => $line_count,
        words => $word_count,
        chars => $char_count
    };
}

print "请输入文件名: ";
chomp(my $filename = <STDIN>);

if (-e $filename) {
    my $stats = count_file_stats($filename);
    
    print "=== 文件统计 ===\n";
    print "文件名: $filename\n";
    print "行数: $stats->{lines}\n";
    print "单词数: $stats->{words}\n";
    print "字符数: $stats->{chars}\n";
} else {
    print "文件不存在\n";
}

示例 2:日志文件分析器

perl
#!/usr/bin/perl
use strict;
use warnings;

sub parse_log_line {
    my ($line) = @_;
    
    if ($line =~ /\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(\w+)\] (.*)/) {
        return {
            timestamp => $1,
            level => $2,
            message => $3
        };
    }
    
    return undef;
}

sub analyze_log {
    my ($filename) = @_;
    
    open(my $fh, '<', $filename) or die "Cannot open $filename: $!";
    
    my %level_counts;
    my @errors;
    
    while (my $line = <$fh>) {
        my $entry = parse_log_line($line);
        
        if ($entry) {
            $level_counts{$entry->{level}}++;
            
            if ($entry->{level} eq 'ERROR') {
                push @errors, $entry;
            }
        }
    }
    
    close($fh);
    
    return {
        level_counts => \%level_counts,
        errors => \@errors
    };
}

my $log_file = 'app.log';

if (-e $log_file) {
    my $analysis = analyze_log($log_file);
    
    print "=== 日志分析 ===\n";
    print "日志级别统计:\n";
    
    foreach my $level (sort keys %{$analysis->{level_counts}}) {
        printf "  %s: %d\n", $level, $analysis->{level_counts}{$level};
    }
    
    print "\n错误日志:\n";
    foreach my $error (@{$analysis->{errors}}) {
        printf "  [%s] %s\n", $error->{timestamp}, $error->{message};
    }
} else {
    print "日志文件不存在\n";
}

示例 3:CSV 文件处理

perl
#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV;

my $csv = Text::CSV->new({ binary => 1, auto_diag => 1 });

# 读取 CSV
open(my $fh, '<', 'data.csv') or die "Cannot open file: $!";

my @headers = @{$csv->getline($fh)};
my @rows;

while (my $row = $csv->getline($fh)) {
    my %data;
    @data{@headers} = @$row;
    push @rows, \%data;
}

close($fh);

# 显示数据
print "=== CSV 数据 ===\n";
foreach my $row (@rows) {
    print "-" x 40 . "\n";
    foreach my $header (@headers) {
        printf "%-15s: %s\n", $header, $row->{$header};
    }
}

# 写入 CSV
open(my $out, '>', 'output.csv') or die "Cannot create file: $!";
$csv->print($out, \@headers);

foreach my $row (@rows) {
    my @values = map { $row->{$_} } @headers;
    $csv->print($out, \@values);
}

close($out);

小结

本章节学习了 Perl 的文件操作:

  1. ✅ 打开和关闭文件
  2. ✅ 读取文件(逐行、整体、二进制)
  3. ✅ 写入文件
  4. ✅ 文件测试
  5. ✅ 文件和目录操作
  6. ✅ 二进制文件操作

接下来,我们将学习 Perl 目录操作