在Perl中正确检测文件的行尾?

时间:2012-08-28 22:15:02

标签: perl newline

问题:我在Windows和* nix上都生成了数据(主要是CSV格式),主要在* nix上处理。 Windows使用CRLF进行行结束,Unix使用LF。对于任何特定文件,我不知道它是否有窗口或* nix行结尾。到目前为止,我一直在写这样的东西来处理差异:

while (<$fh>){
    tr/\r\n//d;
    my @fields = split /,/, $_;
    # ...
}

在* nix上,\ n部分相当于chomping,如果是windows生成的文件,则另外删除\ r \ n(CR)。

但是现在我想要Text :: CSV_XS b / c我开始使用带引号的数据获取更奇怪的数据文件,可能还有嵌入式换行符等。为了让这个模块读取这些文件,Text: :CSV_XS :: getline()要求您指定行尾字符。 (我无法读取上面的每一行,tr / \ n \ r // d,然后用Text :: CSV b / c解析它不能正确处理嵌入式换行符)。我如何正确检测任意文件是否使用windows或* nix样式行结尾,所以我可以告诉Text :: CSV_XS :: eol()如何chomp()?

我在CPAN上找不到简单检测行结尾的模块。我不想首先通过dos2unix转换我的所有数据文件,b / c文件很大(数百千兆字节),并且每个文件花费10多分钟来处理这么简单的事情似乎很愚蠢。我想过编写一个函数来读取文件的前几百个字节并计算LF和CRLF,但我拒绝相信这没有更好的解决方案。

任何帮助?

注意:所有文件都具有完全的Windows行结尾或* nix结尾,即它们不会混合在一个文件中。

5 个答案:

答案 0 :(得分:9)

您可以使用:crlf PerlIO layer打开文件,然后告诉Text::CSV_XS使用\n作为行结束字符。这将静默地将任何CR / LF对映射到单行换行,但这可能是您想要的。

use Text::CSV_XS;
my $csv = Text::CSV_XS->new( { binary => 1, eol => "\n" } );

open( $fh, '<:crlf', 'data.csv' ) or die $!;

while ( my $row = $csv->getline( $fh ) ) {
     # do something with $row
}

答案 1 :(得分:5)

从Perl 5.10开始,您可以使用它来检查一般行结尾,

s/\R//g;

它应该适用于所有情况,包括* nix和Windows。

答案 2 :(得分:3)

读入每个文件的第一行,查看其最后一个字符。如果是\r,则该文件来自Windows,如果不是,则为* nix。然后seek到开始并开始处理。

如果某个文件可能有混合行结尾(例如嵌入换行符的类型不同),则只能猜测。

答案 3 :(得分:1)

理论上,行结尾无法可靠地确定:此文件是否为带有嵌入\n的DOS行结尾的单行,或者这是一堆最后带有一些流浪\r个字符的行一些线?

foo\n
ba\r\n

foo\nba\r\n

如果统计分析不是一个选项,因为它太不准确和昂贵(扫描这么大的文件需要时间),你必须知道编码是什么。

如果您可以控制生产应用程序或使用某种元数据来跟踪生成数据的平台,那么最好指定确切的文件格式。

在Perl中,字符\n表示依赖于语言环境:* nix机器上的\n / \012,旧Mac上的\r / \015和DOS后代即Windows上的序列\r\n / \015\012。因此,为了进行可靠的处理,您应该使用八进制值。

答案 4 :(得分:1)

您可以使用PERLIO变量。这样做的好处是无需根据平台修改脚本的源代码。

如果您正在处理DOS文本文件,请将环境变量PERLIO设置为:unix:crlf

$ PERLIO=:unix:crlf my-script.pl dos-text-file.txt

如果您主要处理DOS文本文件(例如在Cygwin上),您可以将其放在.bashrc中:

export PERLIO=:unix:crlf

(我认为该值应该是Cygwin上PERLIO的默认值,但显然不是。)