Perl 文本处理入门

FreeGuideOnline 14阅读 2026-07-11

bash perl script.pl


执行单行命令:
```bash
perl -e 'print "Hello, Perl!\n";'

基础字符串与打印

Perl 的语法松弛而强大。字符串可以用单引号或双引号,双引号支持转义字符和变量内插。

my $name = "世界";
print "你好,$name!\n";    # 输出:你好,世界!
print '你好,$name!\n';    # 输出:你好,$name!\n(单引号不解析)

常用输出函数:

  • print:普通输出,不自动换行
  • say:自动换行输出,需使用 use feature 'say';use v5.10;

文件读写:文本处理的第一步

读取整个文件

use strict;
use warnings;

my $filename = "data.txt";
open(my $fh, '<', $filename) or die "无法打开文件 '$filename': $!";
my @lines = <$fh>;          # 将文件所有行读入数组
close($fh);

foreach my $line (@lines) {
    print $line;            # 逐行输出
}

逐行读取(节省内存)

while (my $line = <$fh>) {
    chomp($line);           # 去掉行尾换行符
    # 处理 $line
}

写入文件

open(my $out_fh, '>', 'output.txt') or die $!;
print $out_fh "这是第一行\n";
print $out_fh "这是第二行\n";
close($out_fh);

快速单行处理 Perl 命令行提供便利的 -n-p 选项,自动遍历输入并执行代码。

# 打印文件每一行(类似 cat)
perl -ne 'print' data.txt

# 给每一行加行号后输出
perl -ne 'print "$. $_"' data.txt

# 将文件中的所有 "foo" 替换为 "bar" 并写回
perl -pi -e 's/foo/bar/g' data.txt

正则表达式:Perl 的看家本领

正则表达式是文本处理的核心,Perl 提供 =~ 操作符用于匹配、替换和转换。

匹配

my $str = "hello123world";
if ($str =~ /(\d+)/) {
    print "找到数字: $1\n";   # 输出: 找到数字: 123
}

常用匹配修饰符:

  • /i:忽略大小写
  • /g:全局匹配(用于替换时全部替换)
  • /m:多行模式,^$ 匹配行首行尾
  • /s:点号匹配包括换行符

替换

my $text = "I love Perl. Perl is great.";
$text =~ s/Perl/Python/r;   # 只替换第一个,/r 返回新字符串不修改原值
$text =~ s/Perl/Python/g;   # 全局替换

捕获组与后向引用

# 交换单词顺序
my $name = "John Doe";
$name =~ s/(\w+)\s+(\w+)/$2, $1/;
print $name;   # 输出: Doe, John

常用正则模式速查

模式 含义
\d 数字 [0-9]
\w 单词字符 [a-zA-Z0-9_]
\s 空白字符(空格、制表等)
. 除换行外的任意字符
* 零次或多次
+ 一次或多次
? 零次或一次
{n,m} n 到 m 次
^ 字符串或行首
$ 字符串或行尾

内置函数:split、join、grep、map

split —— 字符串分割

my $csv = "apple,banana,grape";
my @fruits = split /,/, $csv;          # 得到数组 ('apple', 'banana', 'grape')

按空白分割(保留单词):

my $line = "   hello   world   ";
my @words = split ' ', $line;          # 自动处理前后空格,结果 ('hello', 'world')

join —— 数组连接

my @parts = ("2024", "12", "25");
my $date = join "-", @parts;          # "2024-12-25"

grep —— 从列表中筛选

grep 与 Linux 的同名命令不同,它是一个函数,用于过滤列表元素。

my @numbers = (1, 2, 3, 4, 5, 6);
my @even = grep { $_ % 2 == 0 } @numbers;   # 得到 (2, 4, 6)

结合正则:

my @lines = ("error: disk full", "info: start", "error: timeout");
my @errors = grep { /error/ } @lines;

map —— 转换列表

将每个元素按照块或表达式变换,返回新列表。

my @nums = (1, 2, 3);
my @squared = map { $_ * $_ } @nums;       # (1, 4, 9)

# 提取文件中每行的第一个字段
my @first_fields = map { (split)[0] } <$fh>;

实战案例:典型文本处理任务

1. 分析 Nginx 日志,统计访问量最高的 IP

假设日志格式类似:

192.168.1.1 - - [25/Dec/2024:10:00:00 +0800] "GET /index.html HTTP/1.1" 200 ...

取出 IP 并计数:

my %count;
while (<>) {
    if (/^(\S+)/) {       # \S+ 匹配非空白字符,即 IP
        $count{$1}++;
    }
}
# 按频次降序输出
foreach my $ip (sort { $count{$b} <=> $count{$a} } keys %count) {
    print "$ip 访问了 $count{$ip} 次\n";
}

运行时将日志文件作为输入:

perl analyze.pl access.log

2. CSV 数据清洗

将文件中的空字段替换为 "N/A",并去除每行末尾的多余逗号。

while (<>) {
    s/,,/,N\/A,/g;               # 将连续逗号替换为 ,N/A,
    s/,$/,N\/A/;                 # 行尾单独的逗号
    s/,\s*$/,N\/A/;              # 处理可能存在的尾随空白
    print;
}

3. 多行文本合并(例如:每三行合并为一行)

my $line_count = 0;
my @buffer;
while (<>) {
    chomp;
    push @buffer, $_;
    $line_count++;
    if ($line_count == 3) {
        print join(", ", @buffer), "\n";
        @buffer = ();
        $line_count = 0;
    }
}
# 处理剩余不足三行的情况
if (@buffer) {
    print join(", ", @buffer), "\n";
}

4. 提取 HTML/XML 中的标签内容(简单场景)

my $html = '<title>Perl入门教程</title>';
if ($html =~ /<title>(.*?)<\/title>/) {
    print "标题是: $1\n";
}

注意:复杂 HTML 建议使用专门的解析模块(如 HTML::Parser),正则不是最佳选择。


常用命令行单行示例

# 反转文件每一行中的字符
perl -lne 'print scalar reverse $_' file.txt

# 统计文件字数,类似 wc
perl -lne 'END {print $words} $words += scalar split' file.txt

# 删除所有空行并输出
perl -ne 'print unless /^$/' file.txt

# 将制表符替换为空格
perl -pe 's/\t/  /g' file.txt

# 打印第10到第20行
perl -ne 'print if 10..20' file.txt

调试与最佳实践

  1. 始终启用 strict 和 warnings
    在脚本开头添加:
    use strict;
    use warnings;