繁体   English   中英

在Perl中逐行处理“ chomp”的奇怪行为

[英]Strange behavior of 'chomp' for processing a file line-by-line in Perl

我正在使用以下Perl脚本进行一些简单处理:

use strict;
my $file = "data-text";
open(FILE, "<$file") or die "Can't open $file: $!\n";
my @lines = <FILE>;
close FILE;
my @arrayA = (); my @arrayB=();
my $i = 0;
while($i < @lines) {
    print $lines[$i], "\t", $lines[$i+1], "\n";
    chomp($lines[$i]); chomp($lines[$i+1]); #The problem is here...
    push @arrayA, \$lines[$i];
    push @arrayB, \$lines[$i+1];
    print $lines[$i], "\t", $lines[$i+1], "\n";
    $i+=2;
}

正如我在脚本中指出的那样,问题出在chomp($lines[$i]); chomp($lines[$i+1]); chomp($lines[$i]); chomp($lines[$i+1]); 看来,如果我使用此行,这些行将被弄乱。

怎么了? 为什么是这样?

chomp从字符串末尾删除单个\\n字符。

如果字符串以\\r\\n结尾(Windows样式行结尾),则chomp将把\\r保留在原位。 这可能会导致出现与您所见类似的症状。

编辑

一些背景。 类似Unix的系统(包括Linux)使用单个换行字符( '\\n' )标记文本文件中每行的结尾。 Windows(及其前身MS-DOS)使用两个字符,回车符和换行符( \\r\\n )。

Perl的许多功能旨在与文本一起使用。 相当合理地,这意味着Perl默认假定它正在读取的任何文本文件都使用底层操作系统的本机行尾表示。

从C继承的Perl的功能是,当读取一行文本时,本机的行尾序列(无论它是什么)都转换为单个'\\n'字符。 (反向转换在输出上完成)。 这使大多数程序不必担心文本的表示方式。 它是在输入和输出的规范内部形式之间来回转换的。 (出于历史原因,这种形式恰好与Unix格式匹配。)

但是,如果您需要处理非本地文本文件,那并没有太大帮助。 如果您在类似Unix的环境中运行,但是正在读取Windows格式的文本文件,则\\r字符看起来像是行的一部分。 特别是, chomp不会对他们做任何特别的事情。 而且,当您打印\\r字符时,通常会导致光标移动到当前行的开头而不前进到下一行。 一团糟。 (Cygwin是此类混乱的丰富来源;它是一种类似Unix的环境,默认情况下使用Unix样式的文本文件,但是它在Windows下运行,并且对Windows文件系统具有完全可见性。您在使用Cygwin吗?)

参见@BillRupert的评论; 他在Windows下运行,并带有Windows本机实现的Perl,因此他看不到您遇到的问题。

如果要处理非本机文本文件,则需要做一些额外的工作。 例如,当阅读一行文字时,

chomp $line;

您可能会写:

chomp $line;
$line =~ s/\r$//;

在编写文本时,您可以执行以下操作:

$line =~ s/$/\r/;

但是首先,您需要确定是要编写Windows样式还是Unix样式的行尾输出。 这很棘手。

(可能有一个Perl模块可以简化此过程;任何知道的人,请在评论中提及它。)

顺便说一句,您看到的输出不是程序正在产生的输出。 如果通过以可打印形式显示不可打印字符的内容过滤输出,则会在输出中看到\\r^M 使用... | cat -A ... | cat -A... | cat -v ... | cat -v如果系统具有cat命令)。

如果可能的话,您可以在尝试阅读输入内容之前先进行翻译。

由于我没有您的数据文件,因此无法确定,但是首先,让我们切换到现代的open和handles,让我们使用警告,也许只是切掉整个数组:

use strict;
use warnings;

## If line endings are the problem, try for example:
#local $/ = "\r\n";

my $file="data-text";

my @lines;
{
    open(my $fh, "<", $file) or die "Can't open $file: $!\n";
    @lines = <$fh>;
}

chomp @lines;

my @arrayA;
my @arrayB;
my $i = 0;
while ($i < @lines) {
    print $lines[$i],"\t",$lines[$i+1],"\n";
    push @arrayA, \$lines[$i];
    push @arrayB, \$lines[$i+1];

    ## The following line is now no different from the above, commented out
    #print $lines[$i],"\t",$lines[$i+1],"\n";
    $i+=2;
}

看看这是否还能满足您的期望。 如果您给我们一部分文件,也许我们会注意到更多。

另外,如果您正在尝试将所有其他行拆分为两个数组,则可以执行以下操作:

while (@lines) {
    my $line1 = shift @lines;
    my $line2 = shift(@lines) || '';
    print $line1,"\t",$line2,"\n";
    push @arrayA, $line1;
    push @arrayB, $line2;
}

内存使用较少。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM