Я поддерживаю 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:)
Каково ваше мнение о наиболее аккуратном способе отбросить родовые разрывы? Что было бы наиболее эффективным?
Изменить: небольшое уточнение - метод "процесс" получает список строк из какого-то, а не читаемого из файла. Каждая строка может иметь
После того, как вы немного раскалываете perlre, я представлю свое лучшее предложение до сих пор, которое, кажется, работает довольно хорошо, Perl 5.10 добавил класс символа \R как обобщенную строку:
$line =~ s/\R//g;
Это то же самое, что:
(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])
Я еще раз задержу этот вопрос, просто посмотрю, есть ли еще более изысканные способы ожидания, чтобы их предложили.
Всякий раз, когда я просматриваю ввод и хочу удалить или заменить символы, я запускаю его через небольшие подпрограммы, подобные этому.
sub clean {
my $text = shift;
$text =~ s/\n//g;
$text =~ s/\r//g;
return $text;
}
Возможно, это не фантазия, но этот метод работает безупречно для меня в течение многих лет.
Чтение perlport Я бы предложил что-то вроде
$line =~ s/\015?\012?$//;
чтобы быть в безопасности для любой платформы, на которой вы находитесь, и любого стиля линии, который вы можете обрабатывать, потому что то, что в \r и\n может различаться с помощью разных вкусов Perl.
Примечание от 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}
$line =~ s/[\r\n]+//g;
Чтобы расширить 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', поэтому он продолжает находить двойные пробелы, пока не заменит их все. (Эффективно заменяя что-то большее, чем одно пространство)
В вашем примере вы можете просто пойти:
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