String-Ersetzung mit einem Wörterbuch


8

Was ist ein guter Weg, um Zeichenfolgen in einer Datei mithilfe eines Wörterbuchs mit vielen Substituenten-Substituenten-Paaren zu ersetzen? Und mit viel meine ich eigentlich ungefähr 20 - nicht viel, aber viel genug, um sie ordentlich zu organisieren.

Ich möchte irgendwie alle Substituenten-Substituenten-Paare in einer Datei dictionary.txtauf einfach zu verwaltende Weise sammeln , da ich viele Dinge ersetzen muss, sagen wir:

"yes"      : "no"
"stop"     : "go, go, go!"
"wee-ooo"  : "ooooh nooo!"
"gooodbye" : "hello"

"high"     : "low"
"why?"     : "i don't know"

Jetzt möchte ich diese Ersetzungen in einer Datei anwenden novel.txt.

Dann möchte ich ausführen, magiccommand --magicflags dictionary.txt novel.txtdamit alle Instanzen von yesin novel.txtdurch ersetzt werden no(also Bayesianwürde sogar durch ersetzt werden Banoian) und alle Instanzen von goodbyein novel.txtdurch ersetzt werden hellound so weiter.

Bisher sind die Saiten , die ich brauche zu ersetzen (und ersetzen) noch nicht haben keine Anführungszeichen (weder einzelne noch doppelt) in ihnen. (Es wäre jedoch schön zu sehen, dass eine Lösung natürlich gut mit Zeichenfolgen funktioniert, die Anführungszeichen enthalten.)

Ich kenne sedund awk/ oder gawkkann solche Sachen hauptsächlich machen, aber können sie auch mit solchen Wörterbuchdateien arbeiten? Scheint, als gawkwäre der richtige Kandidat für magiccommand, was sind die richtigen magicflags? Wie muss ich meine formatieren dictionary.txt?


1
Ist Perl akzeptabel? Es gibt einen guten Trick, um eine Reihe von Such- und Ersetzungsmustern zusammenzustellen.
Sobrique

Antworten:


3

Hier ist ein Weg mit sed:

sed '
s|"\(.*\)"[[:blank:]]*:[[:blank:]]*"\(.*\)"|\1\
\2|
h
s|.*\n||
s|[\&/]|\\&|g
x
s|\n.*||
s|[[\.*^$/]|\\&|g
G
s|\(.*\)\n\(.*\)|s/\1/\2/g|
' dictionary.txt | sed -f - novel.txt

So funktioniert es:
Die erste sedwird dictionary.txtzu einer Skriptdatei (Bearbeitungsbefehle, einer pro Zeile). Dies wird an die 2. weitergeleitet sed(beachten Sie, -f -was bedeutet, dass Befehle von gelesen werden stdin), die diese Befehle ausführt und bearbeitet novel.txt.
Dies erfordert die Übersetzung Ihres Formats

"STRING"   :   "REPLACEMENT"

in einen sedBefehl und das Entkommen von Sonderzeichen im Prozess für beide LHSund RHS:

s/ESCAPED_STRING/ESCAPED_REPLACEMENT/g

Also die erste Auswechslung

s|"\(.*\)"[[:blank:]]*:[[:blank:]]*"\(.*\)"|\1\
\2|

wird "STRING" : "REPLACEMENT"zu STRING\nREPLACEMENT( \nist ein Zeilenumbruchzeichen). Das Ergebnis wird dann über den halten Raum kopiert .
s|.*\n||löscht der erste Teil keeping nur REPLACEMENTdann s|[\&/]|\\&|gdie reservierten Zeichen entweicht (das ist die RHS).
Es xändert dann den Haltepuffer mit dem Musterraum und s|\n.*||löscht nur den zweiten Teil, der beibehalten wird, STRINGund s|[[\.*^$/]|\\&|gführt die Escape-Aktion aus (dies ist der LHS).
Der Inhalt des Haltepuffers wird dann über an den Musterraum angehängt, Gso dass nun der Inhalt des Musterraums ist ESCAPED_STRING\nESCAPED_REPLACEMENT.
Die endgültige Auswechslung

s|\(.*\)\n\(.*\)|s/\1/\2/g|

verwandelt es in s/ESCAPED_STRING/ESCAPED_REPLACEMENT/g


1

Hier ist eine Perl-Version. Es erstellt einen Hash mit vorkompilierten regulären Ausdrücken und durchläuft dann jede Eingabezeile, wobei alle regulären Ausdrücke auf jede Zeile angewendet werden. perl's -iwird für die "In-Place-Bearbeitung" der Eingabedatei verwendet. Sie können problemlos reguläre Ausdrücke oder Ersatzzeichenfolgen hinzufügen oder ändern.

Das Vorkompilieren der regulären Ausdrücke mithilfe von qr//verbessert die Geschwindigkeit des Skripts erheblich. Dies macht sich besonders bemerkbar, wenn viele reguläre Ausdrücke und / oder viele Eingabezeilen verarbeitet werden müssen.

#! /usr/bin/perl -i

use strict;

# the dictionary is embedded in the code itself.
# see 2nd version below for how to read dict in
# from a file.
my %regex = (
    qr/yes/      => 'no',
    qr/stop/     => 'go, go, go!',
    qr/wee-ooo/  => 'ooooh nooo!',
    qr/gooodbye/ => 'hello',
    qr/high/     => 'low',
    qr/why\?/    => 'i don\'t know',
);

while (<>) {
      foreach my $key (keys %regex) {
            s/$key/$regex{$key}/g;
      }
}

Hier ist eine andere Version, die das Wörterbuch vom ersten Dateinamen in der Befehlszeile einliest, während der zweite (und optionale nachfolgende) Dateinamen noch verarbeitet wird:

#! /usr/bin/perl -i

use strict;

# the dictionary is read from a file.
#
# file format is "searchpattern replacestring", with any
# number of whitespace characters (space or tab) separating
# the two fields.  You can add comments or comment out dictionary
# entries with a '#' character.
#
# NOTE: if you want to use any regex-special characters as a
# literal in either $searchpattern or $replacestring, you WILL
# need to escape them with `\`.  e.g. for a literal '?', use '\?'.
#
# this is very basic and could be improved.  a lot.

my %regex = ();

my $dictfile = shift ;
open(DICT,'<',$dictfile) || die "couldn't open $dictfile: $!\n";
while(<DICT>) {
    s/#.*// unless (m/\\#/); # remove comments, unless escaped.
                             # easily fooled if there is an escaped 
                             # '#' and a comment on the same line.

    s/^\s*|\s*$//g ;         # remove leading & trailing spaces
    next if (/^$/) ;         # skip empty lines

    my($search, $replace) = split;
    $regex{qr/$search/} = $replace;
};
close(DICT);


# now read in the input file(s) and modify them.
while (<>) {
      foreach my $key (keys %regex) {
            s/$key/$regex{$key}/g;
      }
}

1

Ich habe angefangen, dies als Kommentar zu schreiben, aber es wurde zu kompliziert, daher eine zweite Perl-Antwort. In Anbetracht Ihrer Quelldatei können Sie einen ordentlichen Perl-Trick verwenden, um einen regulären Ausdruck zu erstellen:

#!/usr/bin/env perl

use strict;
use warnings; 
use Data::Dumper;

#build key-value pairs
my %replace = map { /"(.+)"\s*:\s*"(.+)"/ } <DATA>;
print Dumper \%replace; 

#take the keys of your hash, then build into capturing regex
my $search = join ( "|", map {quotemeta} keys %replace ); 
$search = qr/($search)/;

print "Using match regex of: $search\n";

#read stdin or files on command line, line by line
while ( <> ) { 
    #match regex repeatedly, replace with contents of hash. 
    s/$search/$replace{$1}/g;
    print;
}

__DATA__
"yes"      : "no"
"stop"     : "go, go, go!"
"wee-ooo"  : "ooooh nooo!"
"gooodbye" : "hello"

"high"     : "low"
"why?"     : "i don't know"

Wir generieren einen Hash mithilfe einer mehrzeiligen Musterübereinstimmung und maperstellen Schlüsselwertpaare.

Wir erstellen einen Such-Regex und verwenden die darin erfassten Werte, um ihn zu ersetzen.

Verwenden <>ist das magische Dateihandle von Perl - STDINoder Dateien, die in der Befehlszeile angegeben sind. Viel wie sed macht es. (Sie können eine Datei verwenden und sie 'normal' für das Muster lesen. Die Verwendung von DATAdient nur zur Veranschaulichung.)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.