Замяна на Perl - проблем с справочна таблица

Имам ~35 MB KML файл, в който всички показалци са наречени „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), получавам някой от 3-те резултата по-долу:

<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}"/; 

Ще работи, тъй като ще съвпада само ако точният шаблон е ограден в кавички.

Малко е да се замислите, но четенето на тази страница за регулярните изрази ще ви направи майстор при тях. Играта на игри като regex golf също ще ви помогне да подобрите уменията си и да разрешите тези проблеми по-ефективно.

person ahjohnston25    schedule 14.02.2015