Странное поведение 'chomp' для построчной обработки файла в Perl - PullRequest
1 голос
/ 20 октября 2011

Я использую следующий скрипт 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]);. Кажется, если бы я использовал эту строку, строки были бы перепутаны.

Что не так? Почему это?

Ответы [ 2 ]

7 голосов
/ 20 октября 2011

chomp удаляет один \n символ из конца строки.

Если строка заканчивается на \r\n (конец строки в стиле Windows), chomp оставит \r на месте. Это может привести к появлению симптомов, похожих на то, что вы видите.

EDIT :

Некоторый фон. Unix-подобные системы (включая Linux) используют один символ перевода строки ('\n'), чтобы отметить конец каждой строки в текстовом файле. Windows (и ее предшественник MS-DOS) используют два символа: возврат каретки и перевод строки (\r\n).

Многие функции Perl предназначены для работы с текстом. Это означает, что вполне разумно, что Perl предполагает по умолчанию, что любой текстовый файл, который он читает, использует собственное представление конца строки базовой операционной системы.

Особенность Perl, унаследованная от C, заключается в том, что при чтении строки текста нативная последовательность конца строки, какой бы она ни была, транслируется в один '\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 -v, если ваша система имеет команду cat.

Если возможно, вы можете подумать о переводе ввода, прежде чем пытаться его прочитать.

0 голосов
/ 20 октября 2011

Поскольку у меня нет вашего файла данных, я не могу сказать наверняка, но прежде всего, давайте переключимся на современное открытие и дескрипторы, давайте использовать предупреждения и, возможно, просто разбить весь массив:

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;
}

Который использует меньше памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...