簡體   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