Самый аккуратный способ удалить линейные разбивки в Perl
-
22-08-2019 - |
Вопрос
Я поддерживаю сценарий, который может получить свой вклад из различных источников, и работает над ним по линии. В зависимости от фактического используемого источника, Line Breaks может быть в стиле UNIX, в стиле Windows или даже для некоторого агрегированного входа, смешанного (!).
При чтении из файла это идет примерно так:
@lines = <IN>;
process(\@lines);
...
sub process {
@lines = shift;
foreach my $line (@{$lines}) {
chomp $line;
#Handle line by line
}
}
Итак, что мне нужно сделать, так это заменить Chomp чем-то, что удаляет либо линейные разбивки в стиле UNIX, либо в стиле Windows. Я приду в слишком много способов решения этого, один из обычных недостатков Perl :)
Каково ваше мнение о самом аккуратном способе отмены общих линейных разбил? Что будет наиболее эффективным?
Изменить: небольшое разъяснение - метод «процесс» получает список линий откуда, не чтение из файла. Анкет Каждая строка могла бы иметь
- Никаких зацепленных линейных разбил
- Linebreaks в стиле Unix
- LineBreaks в стиле Windows
- Просто возврат перевозки (когда оригинальные данные имеют линейные разбивки в стиле Windows и читаются с $/ = ' n')
- Агрегированный набор, где линии имеют разные стили
Решение
После копания через Перлр Документы немного представлю свое лучшее предложение, которое, кажется, работает довольно хорошо. 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;
}
Это может быть не модным, но этот метод работал безупречным для меня годами.
Чтение Перлпорт Я бы предложил что -то вроде
$line =~ s/\015?\012?$//;
Чтобы быть в безопасности для любой платформы, в которой вы находитесь, и в любом стиле линейки, вы можете обрабатывать, потому что то, что находится в r и n, может отличаться в результате различных ароматов Perl.
$line =~ s/[\r\n]+//g;
Примечание с 2017 года: File :: Slurp не рекомендуется из -за ошибок в проектировании и невозможных ошибок. Использовать File :: Splurper или же Путь :: Крошечный вместо.
Расширение вашего ответа
use File::Slurp ();
my $value = File::Slurp::slurp($filename);
$value =~ s/\R*//g;
Файл :: Slurp устраняет файл io io и просто возвращает для вас строку.
ПРИМЕЧАНИЕ
Важно отметить добавление
/g
, без этого, с учетом многострочной строки, она только заменит первый оскорбительный персонаж.Также удаление
$
, что является избыточным для этой цели, как мы хотим раздеться все разрывы линии, а не просто разбиты линии перед тем, что подразумевалось под$
На этой ОС.В многострочной строке,
$
соответствует конец нить и это было бы проблематично).Точка 3 означает, что точка 2 сделана с предположением, что вы также хотите использовать
/m
В противном случае '$' было бы в основном бессмысленным для чего -либо практического в строке с> 1 строками или, выполнять обработку одной линии, ОС, которая на самом деле понимает$
и удается найти\R*
это продолжается$
Примеры
while( my $line = <$foo> ){
$line =~ $regex;
}
Учитывая приведенную выше нотацию, ОС, которая не понимает, какие ваши файлы ' n' или ' r' делимитеры, в сценарии по умолчанию с набором разделителей ОС по умолчанию $/
приведет к чтению всего вашего файла в качестве одной смежной строки (если в вашей строке нет делимитов $ ОС, где это будет разграничить это)
Так что в этом случае все эти корпорации бесполезны:
/\R*$//
: Будет только стереть последнюю последовательность\R
в файле/\R*//
: Будет только стереть первую последовательность\R
в файле/\012?\015?//
: Когда только стереть первое012\015
,\012
, или же\015
последовательность,\015\012
приведет к любому\012
или же\015
излучение./\R*$//
: Если в файле не было никаких байтовых последовательностей ' 015 $ osdelimiter', то тогда НЕТ LineBreaks будут удалены, за исключением собственных ОС.
Казалось бы, никто не получает то, о чем я говорю, так что вот пример кода, то есть протестирован к НЕТ Снимите линейные подачи. Запустите его, вы увидите, что он оставляет линейную корпус.
#!/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 };
}
}
Для ЧЕТКО Необработанный вывод, см. Здесь: http://pastebin.com/f2c063d74
Обратите внимание, что есть определенные комбинации, которые, конечно, работают, но они, вероятно, те, которые вы сами надо, протестированы.
Обратите внимание, что на этом выводе все результаты должны иметь форму >|$string|<>|$string|<
с Нет линейных кормов считаться допустимым выводом.
а также $string
имеет общую форму {0}$data{1}$delimiter{2}
Где во всех источниках вывода должен быть либо:
- Ничего между
{1}
а также{2}
- Только
|<>|
между{1}
а также{2}
В вашем примере вы можете просто пойти:
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
Чтобы расширить ответ Теда Кэмброна выше, и что -то, что здесь не было адресовано: если вы удалите все разрывы строк без разбора из куски введенного текста, вы в конечном итоге получите параграфы, столкнувшись друг с другом без пробелов, когда вы выводите этот текст позже. Это то, что я использую:
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, поэтому он продолжает находить двойные пространства, пока не заменит их все. (Эффективно заменить что -нибудь еще, что одно пространство)