5

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

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

在 *nix 上,\n 部分相当于 chomping,如果它是 Windows 生成的文件,则另外去掉 \r (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 文件很大(数百 GB),每个文件花费 10 多分钟来处理如此简单的事情似乎很愚蠢。我想过编写一个函数来读取文件的前几百个字节并计算 LF 和 CRLF,但我拒绝相信这没有更好的解决方案。

有什么帮助吗?

注意:所有文件要么完全以windows-line 结尾,要么以*nix 结尾,也就是说,它们不会混合在一个文件中。

4

5 回答 5

10

您可以使用:crlf PerlIO 层打开文件,然后告诉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
}
于 2012-08-28T22:53:07.803 回答
6

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

s/\R//g;

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

于 2012-08-28T22:42:08.033 回答
3

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

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

于 2012-08-28T22:30:51.040 回答
1

理论上无法可靠地确定行尾:这个文件是单行,带有 DOS 行尾并带有 embeded \ns,还是这是一堆行,\r在某些行的末尾有一些杂散字符?

foo\n
ba\r\n

相对

foo\nba\r\n

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

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

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

于 2012-08-28T22:38:56.037 回答
1

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

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

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

如果你主要处理 DOS 文本文件(例如在 Cygwin 上),你可以把它放在你的.bashrc:

export PERLIO=:unix:crlf

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

于 2014-01-23T22:31:59.363 回答