Замена Perl — проблема с таблицей поиска

У меня есть файл KML размером ~ 35 МБ, в котором все метки имеют имена «kml1234» и тому подобное. Я хочу заменить имя удобочитаемой строкой, например «Зона 9987», и у меня есть таблица поиска. Я нашел фрагмент perl здесь (https://stackoverflow.com/a/6435950), и он работал для большинства метки. Однако я обнаружил, что в определенных случаях это не сработало. Вот код.

$repl{kml1} = "Area A";
$repl{kml12} = "Area B";
$repl{kml123} = "Area C";
$repl{kml69} = "Area D";
$repl{kml4458} = "Area E";

$s = <<HEADER;
\$start = time;
open(F, "input.txt");
open(OUTPUT, ">output.txt");
while (<F>) {
HEADER

foreach $key (keys %repl) {
   $s .= "s/$key/$repl{$key}\/;\n"
}

$s .= <<FOOTER;
print \$_;
}
close(F);
close(OUTPUT);
print "Elapsed time (eval.pl): " . (time - \$start) . "\r\n";
FOOTER

eval $s;

Я протестировал это изолированно, используя тестовую строку (помещенную в input.txt):

<Placemark id="kml123">

Ожидаемый результат от этого:

<Placemark id="Area C">

Однако, если я снова запущу скрипт с теми же входными данными (kml123), я получу один из трех результатов ниже:

<Placemark id="Area A23">
<Placemark id="Area B3">
<Placemark id="Area C">

Кажется, что подстановка каким-то образом усекает $key только до kml1 или kml12 иногда? Я замечаю, что никогда не получаю «Область D» или «Область E», которые ожидаются, и я подозреваю, что это потому, что они недостаточно похожи на kml123, только первые 3. Любые подсказки?


person Jason    schedule 14.02.2015    source источник


Ответы (2)


Основная проблема уже упоминалась в ответе @ahjohnston25, но вы взяли на себя такой уродливый код с оценкой и нечеткими вещами, поэтому я сделал его немного проще и чище:

#!/usr/bin/perl

use strict; use warnings; use autodie;

my %repl = (
  "kml1" => "Area A",
  "kml12" => "Area B",
  "kml123" => "Area C",
  "kml69" => "Area D",
  "kml4458" => "Area E",
);

open( my $F, '<', "input.txt" );
open( my $OUTPUT, '>', "output.txt" );

while ( <$F> ) {
  foreach my $key ( sort keys %repl ) {
     s/\b$key\b/$repl{$key}/g;
  }
  print $OUTPUT $_; 
}

close( $F );
close( $OUTPUT );

Надеюсь, в таком виде намного легче понять, что происходит.

person w.k    schedule 14.02.2015

Ядро этой программы использует регулярные выражения Perl. Проблема в том, что для Perl kml1 в качестве строки поиска (первая часть вашего выражения s///) соответствует kml123. Если вы измените свой поиск/замену на следующее:

s/"$key">$/"$repl{$key}"/; 

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

Немного подумать, но чтение этой страницы о регулярных выражениях сделает вас мастером на них. Такие игры, как регулярный гольф, также помогут вам улучшить свои навыки и решить эти проблемы более эффективно.

person ahjohnston25    schedule 14.02.2015