Как использовать Perl для перемежения символов между последовательными совпадениями с заменой регулярных выражений?
-
22-07-2019 - |
Вопрос
Следующие строки значений, разделенных запятыми, содержат несколько последовательных пустых полей:
$rawData =
"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n"
Я хочу заменить эти пустые поля значениями «Н/Д», поэтому я решил сделать это с помощью замены регулярных выражений.
Я попробовал это в первую очередь:
$rawdata =~ s/,([,\n])/,N\/A/g; # RELABEL UNAVAILABLE DATA AS 'N/A'
который вернулся
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,,N/A,\n
Не то, что я хотел.Проблема возникает, когда встречается более двух последовательных запятых.Регулярное выражение поглощает две запятые за раз, поэтому при повторном сканировании строки оно начинается с третьей запятой, а не со второй.
Я подумал, что это может быть как-то связано с опережением и прогнозированием.утверждения обратного просмотра, поэтому я попробовал следующее регулярное выражение:
$rawdata =~ s/(?<=,)([,\n])|,([,\n])$/,N\/A$1/g; # RELABEL UNAVAILABLE DATA AS 'N/A'
что привело к:
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,N/A,,N/A,,N/A,,N/A\n
Это тоже не сработало.Это просто сдвинуло пары запятых на одну.
Я знаю, что двойная промывка этой строки одним и тем же регулярным выражением даст результат, но это кажется грубым.Конечно, должен быть способ заставить одну замену регулярного выражения выполнить эту работу.Какие-либо предложения?
Окончательная строка должна выглядеть так:
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,N/A,,N/A,N/A,N/A,N/A,N/A\n
Решение
Я не мог до конца понять, что вы пытались сделать в своем примере с просмотром назад, но подозреваю, что вы страдаете от ошибки приоритета и что все, что происходит после просмотра назад, должно быть заключено в (?: ... )
Итак |
не избегает просмотра назад.
Начиная с нуля, то, что вы пытаетесь сделать, звучит довольно просто:поместите N/A после запятой, если за ней следует еще одна запятая или новая строка:
s!,(?=[,\n])!,N/A!g;
Пример:
my $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n";
use Data::Dumper;
$Data::Dumper::Useqq = $Data::Dumper::Terse = 1;
print Dumper($rawData);
$rawData =~ s!,(?=[,\n])!,N/A!g;
print Dumper($rawData);
Выход:
"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n"
"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,N/A,N/A,N/A\n"
Другие советы
РЕДАКТИРОВАТЬ:Обратите внимание, что вы можете открыть дескриптор файла для строки данных и позволить readline
разобраться с окончаниями строк:
#!/usr/bin/perl
use strict; use warnings;
use autodie;
my $str = <<EO_DATA;
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,
EO_DATA
open my $str_h, '<', \$str;
while(my $row = <$str_h>) {
chomp $row;
print join(',',
map { length $_ ? $_ : 'N/A'} split /,/, $row, -1
), "\n";
}
Выход:
E:\Home> t.pl 2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear 2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,N/A,N/A,N/A
Вы также можете использовать:
pos $str -= 1 while $str =~ s{,(,|\n)}{,N/A$1}g;
Объяснение:Когда s///
находит ,,
и заменяет его на ,N/A,
он уже переместился на символ после последней запятой.Таким образом, некоторые последовательные запятые будут пропущены, если вы используете только
$str =~ s{,(,|\n)}{,N/A$1}g;
Поэтому я использовал цикл для перемещения pos $str
обратно на персонажа после каждой успешной замены.
Теперь, как @ysth шоу:
$str =~ s!,(?=[,\n])!,N/A!g;
сделал бы while
ненужный.
Вы можете искать
(?<=,)(?=,|$)
и замените это на N / A.
Это регулярное выражение соответствует (пустому) пробелу между двумя запятыми или между запятой и концом строки.
Быстрая и грязная версия хака:
my $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n";
while ($rawData =~ s/,,/,N\/A,/g) {};
print $rawData;
Не самый быстрый код, но самый короткий. Это должно пройти через максимум два раза.
Не регулярное выражение, но и не слишком сложное:
$string = join ",", map{ Не регулярное выражение, но и не слишком сложное:
<*>
, - 1
требуется в конце, чтобы split
включал любые пустые поля в конце строки.
eq "" ? "N/A" : Не регулярное выражение, но и не слишком сложное:
<*>
, - 1
требуется в конце, чтобы split
включал любые пустые поля в конце строки.
} split (/,/, $string,-1);
, - 1
требуется в конце, чтобы split
включал любые пустые поля в конце строки.