perl文本处理(尤其是在加载文件时)

时间:2019-02-13 17:40:52

标签: perl text-processing

结合使用grepcutsedawk等,在外壳中通常很容易加载文件和排序列。

但是,当我不得不在Perl中执行此操作时,我经常会使用许多拆分(一个接一个的正则表达式)来做冗长而痛苦的事情,结果是看起来像这样的肮脏代码:

open $FH, "<", $file;
@file = <$FH>;
close $FH;
foreach $line (@file) {
    ( $foo, $bar, $some, $thing) = ( split(/,/, $line) )[3,8,9,15] 
    ( $new_some ) = (split(/-/, $some))[2];
    ($new_foo = $foo) =~ s/xx//;
    $uc_bar = uc($bar);
    # and so on.....
}

难道没有更优雅的方式来做这些事情(拆分字段,替换模式等)吗?还是更“快速”的方式(不一定优雅)?

还有一种方法可以在加载时仅加载文件的必需部分(无需加载内存中的所有内容,而是在加载之前进行过滤)?

1 个答案:

答案 0 :(得分:2)

优雅是主观的,但我至少可以回答您的一个问题,并提出一些可能会缩短或改善您的代码的事情。

“有没有一种方法可以在加载时仅加载文件的必需部分” -在显示的代码中,我认为不需要将整个文件加载到内存中。逐行处理文件的典型模式,相当于Perl的-n and -p switches的模式,是这种模式:

open my $fh, '<', $file or die "$file: $!";
while (<$fh>) {          # reads line into $_
    my @fields = split;  # splits $_ on whitespace, like awk
    my ($foo, $bar, $some, $thing) = @fields[3,8,9,15];
    ...
}
close $fh;

我认为这很优雅,但是根据您的写作,我想您正在将其与可能包含100个字符的管道命令的单行代码进行比较。 Perl也可以做到这一点:正如评论中已经提到的,看看开关-n, -p, -a, -F, and -i。如果显示一些具体的示例,您可能会得到一些答复,说明如何使用Perl来缩短操作时间。

但是,如果您要做更多的事情,那么通常最好将其扩展为上面的脚本。恕我直言,将内容放到脚本中可以赋予您更多功能:它不像命令行历史记录那样短暂,它更易于扩展,使用模块也更容易,您可以添加命令行选项,处理多个文件等等。例如,通过以下代码片段,您将获得Text::CSV的全部功能-支持引号,转义,多行字符串等。

use Text::CSV;
my $csv = Text::CSV->new({binary=>1, auto_diag=>2, eol=>$/});
open my $fh, '<', $file or die "$file: $!";
while ( my $row = $csv->getline($fh) ) {
    ...
    $csv->print(select, $row);
}
$csv->eof or $csv->error_diag;
close $fh;

您可能还想查看该模块的csv函数,该函数在短函数中提供了很多功能。如果您仍然认为这只是“痛苦的” “肮脏的” 的全部,并且您希望用更少的代码来完成工作,那么可以使用一些捷径将整个文件插入到存储器my $data = do { local (*ARGV, $/) = $file; <> };或与-i命令行开关相同的示例:

local ($^I, @ARGV) = ('.bak', $file);
while (<>) {
    # s///; or @F=split; or whatever
    print;  # prints $_ back out
}

我喜欢Perl的一件事是,它可以让您以多种不同的方式表达自己的想法-无论是要编写一个非常简短的脚本来完成一项一次性任务,还是编写一个大型OO项目,{ {3}}