Подтвердить что ты не робот

Самый простой способ удаления строк в Perl

Я поддерживаю script, который может получать свои данные из разных источников и работает на нем в каждой строке. В зависимости от используемого источника, разрывы строк могут быть Unix-стилем, стилем Windows или даже для некоторого агрегированного ввода, смешанным (!).

При чтении из файла это выглядит примерно так:

@lines = <IN>;
process(\@lines);

...

sub process {
    @lines = shift;
    foreach my $line (@{$lines}) {
        chomp $line;
        #Handle line by line
    }
}

Итак, мне нужно сделать замену chomp на что-то, что удаляет стили строк Unix-стиля или Windows. Я придумываю слишком много способов решить это, один из обычных недостатков Perl:)

Каково ваше мнение о наиболее аккуратном способе отбросить родовые разрывы? Что было бы наиболее эффективным?

Изменить: небольшое уточнение - метод "процесс" получает список строк из какого-то, а не читаемого из файла. Каждая строка может иметь

  • Отсутствие прерывания строк
  • Линейные строки в стиле Unix
  • Линейные строки в стиле Windows
  • Just Carriage-Return (когда исходные данные имеют разрывы строк в стиле Windows и считываются с помощью $/= '\n')
  • Агрегированный набор, в котором строки имеют разные стили
4b9b3361

Ответ 1

После того, как вы немного раскалываете perlre, я представлю свое лучшее предложение до сих пор, которое, кажется, работает довольно хорошо, Perl 5.10 добавил класс символа \R как обобщенную строку:

$line =~ s/\R//g;

Это то же самое, что:

(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])

Я еще раз задержу этот вопрос, просто посмотрю, есть ли еще более изысканные способы ожидания, чтобы их предложили.

Ответ 2

Всякий раз, когда я просматриваю ввод и хочу удалить или заменить символы, я запускаю его через небольшие подпрограммы, подобные этому.

sub clean {

    my $text = shift;

    $text =~ s/\n//g;
    $text =~ s/\r//g;

    return $text;
}

Возможно, это не фантазия, но этот метод работает безупречно для меня в течение многих лет.

Ответ 3

Чтение perlport Я бы предложил что-то вроде

$line =~ s/\015?\012?$//;

чтобы быть в безопасности для любой платформы, на которой вы находитесь, и любого стиля линии, который вы можете обрабатывать, потому что то, что в \r и\n может различаться с помощью разных вкусов Perl.

Ответ 4

Примечание от 2017 года: Файл:: Slurp не рекомендуется из-за ошибок дизайна и ошибок, которые не были сохранены. Вместо этого используйте File::Slurper или Path::Tiny.

распространяющийся на ваш ответ

use File::Slurp ();
my $value = File::Slurp::slurp($filename);
$value =~ s/\R*//g;

Файл:: Slurp абстрагирует файлы File IO и просто возвращает строку для вас.

Примечание

  • Важно отметить добавление /g, без него, учитывая многострочную строку, это заменит только первый оскорбительный символ.

  • Кроме того, удаление $, которое является избыточным для этой цели, поскольку мы хотим разбить все разрывы строк, а не только разрывы строк до того, что подразумевается под $ в этой ОС.

  • В многострочной строке $ соответствует концу строки, и это будет проблематично).

  • Точка 3 означает, что точка 2 сделана с предположением, что вы также хотите использовать /m иначе "$" будет в основном бессмысленным для чего-либо практического в строке s > 1 строкой или однолинейная обработка, операционная система, которая фактически понимает $ и удается найти \R*, которые отправляют $

Примеры

while( my $line = <$foo> ){
      $line =~ $regex;
}

Учитывая приведенные выше обозначения, ОС, которая не понимает, какие ваши файлы '\n' или '\ r' разделители, в сценарии по умолчанию с разделителем по умолчанию ОС, установленным для $/, приведет к чтению всего вашего файла как одна непрерывная строка (если в вашей строке нет ограничителей $OS в ней, где она будет разделяться)

Итак, в этом случае все эти регулярные выражения бесполезны:

  • /\R*$//: стирается только последняя последовательность \R в файле
  • /\R*//: стирает только первую последовательность \R в файле
  • /\012?\015?//: Когда будет удалена только первая последовательность 012\015, \012 или \015, \015\012 приведет к испусканию как \012, так и \015.

  • /\R*$//: Если в файле не существует байтовых последовательностей '\ 015 $OSDELIMITER', тогда NO-строки будут удалены, кроме собственных ОС.

Казалось бы, никто не понимает, о чем я говорю, вот пример кода, который проверяется, чтобы НЕ удалять фиды строк. Запустите его, вы увидите, что он оставляет строки в.

#!/usr/bin/perl 

use strict;
use warnings;

my $fn = 'TestFile.txt';

my $LF = "\012";
my $CR = "\015";

my $UnixNL = $LF;
my $DOSNL  = $CR . $LF;
my $MacNL  = $CR;

sub generate { 
    my $filename = shift;
    my $lineDelimiter = shift;

    open my $fh, '>', $filename;
    for ( 0 .. 10 )
    {
        print $fh "{0}";
        print $fh join "", map { chr( int( rand(26) + 60 ) ) } 0 .. 20;
        print $fh "{1}";
        print $fh $lineDelimiter->();
        print $fh "{2}";
    }
    close $fh;
}

sub parse { 
    my $filename = shift;
    my $osDelimiter = shift;
    my $message = shift;
    print "Parsing $message File $filename : \n";

    local $/ = $osDelimiter;

    open my $fh, '<', $filename;
    while ( my $line = <$fh> )
    {

        $line =~ s/\R*$//;
        print ">|" . $line . "|<";

    }
    print "Done.\n\n";
}


my @all = ( $DOSNL,$MacNL,$UnixNL);
generate 'Windows.txt' , sub { $DOSNL }; 
generate 'Mac.txt' , sub { $MacNL };
generate 'Unix.txt', sub { $UnixNL };
generate 'Mixed.txt', sub {
    return @all[ int(rand(2)) ];
};


for my $os ( ["$MacNL", "On Mac"], ["$DOSNL", "On Windows"], ["$UnixNL", "On Unix"]){
    for ( qw( Windows Mac Unix Mixed ) ){
        parse $_ . ".txt", @{ $os };
    }
}

Для вывода CLEARLY Unprocessed см. здесь: http://pastebin.com/f2c063d74

Обратите внимание, что есть определенные комбинации, которые, конечно, работают, но они, скорее всего, те, которые вы сами наивно испытали.

Обратите внимание, что в этом выводе все результаты должны иметь вид >|$string|<>|$string|<, при этом NO LINE FEEDS считаться допустимым выходом.

и $string имеет общий вид {0}$data{1}$delimiter{2}, где во всех выходных источниках должно быть либо:

  • Ничего между {1} и {2}
  • только |<>| между {1} и {2}

Ответ 5

$line =~ s/[\r\n]+//g;

Ответ 6

В вашем примере вы можете просто пойти:

chomp(@lines);

Или:

$_=join("", @lines);
s/[\r\n]+//g;

Или:

@lines = split /[\r\n]+/, join("", @lines);

Используя их непосредственно в файле:

perl -e '$_=join("",<>); s/[\r\n]+//g; print' <a.txt |less

perl -e 'chomp(@a=<>);print @a' <a.txt |less

Ответ 7

Чтобы расширить Ted Cambron ответ выше и что-то, что не было рассмотрено здесь: Если вы удалите все разрывы строк без разбора из фрагмента введенного текста, вы получите в результате абзацы, которые работают друг с другом без пробелов, когда вы выведете этот текст позже. Это то, что я использую:

sub cleanLines{

    my $text = shift;

    $text =~ s/\r/ /; #replace \r with space
    $text =~ s/\n/ /; #replace \n with space
    $text =~ s/  / /g; #replace double-spaces with single space

    return $text;
}

Последняя подстановка использует модификатор g 'greedy', поэтому он продолжает находить двойные пробелы, пока не заменит их все. (Эффективно заменяя что-то большее, чем одно пространство)