Gibt es eine effiziente Möglichkeit, Text, der dem gesamten Inhalt einer Datei entspricht, durch den gesamten Inhalt einer anderen Datei zu ersetzen?

7

Ich habe drei Dateien

~/naive-file.txt
~/old-text.txt
~/new-text.txt

Ich möchte jeden Fall finden, in dem der Inhalt von ~/old-text.txtvorkommt, ~/naive-file.txtund diese Vorkommen durch den Inhalt von ersetzen ~/new-text.txt. Ich bin mir sicher, dass dies mit sedoder machbar ist, awkaber ich kann anscheinend nicht den richtigen Befehl finden. Ist das möglich?

Angenommen, der Inhalt von ~/naive-file.txtist

$ cat ~/naive-file.txt
Sed id ligula quis est convallis tempor.

This is the old text.

It might have multiple lines and some special characters like these \ { & % #)!
etc...


Nunc aliquet, augue nec adipiscing interdum, lacus tellus malesuada massa, quis
varius mi purus non odio.

Angenommen, der Inhalt von ~/old-text.txtist

$ cat ~/old-text.txt
This is the old text.

It might have multiple lines and some special characters like these \ { & % #)!
etc...

Angenommen, der Inhalt von ~/new-text.txtist

$ cat ~/new-text.txt
This is the new text.

It could also have multiple lines and special characters like these \ { & %
etc...

Das Ausführen meines gewünschten Befehls würde dann erzeugen

Sed id ligula quis est convallis tempor.

This is the new text.

It could also have multiple lines and special characters like these \ { & %
etc...


Nunc aliquet, augue nec adipiscing interdum, lacus tellus malesuada massa, quis
varius mi purus non odio.
Brian Fitzpatrick
quelle
erste Zeichenfolge von old-text.txtdurch erste Zeichenfolge von ersetzt werden new-text.txtund so weiter?
iruvar
@iruvar Ich werde ein Beispiel hinzufügen, um zu verdeutlichen.
Brian Fitzpatrick
@iruvar Beispiel hinzugefügt.
Brian Fitzpatrick
Ist die Anzahl der Zeilen in alten und neuen Dateien gleich?
Rakesh Sharma
Nein, ich hätte das wahrscheinlich in meinem Beispiel klarstellen sollen.
Brian Fitzpatrick

Antworten:

5

Perl zur Rettung!

Lesen Sie die Ersatzpaare in einen Hash. Lesen Sie dann die Eingabe Zeile für Zeile und versuchen Sie, die Übereinstimmungen zu ersetzen.

#!/usr/bin/perl
use warnings;
use strict;

open my $ot, '<', 'old-text.txt' or die $!;
chomp( my @lines = <$ot> );
open my $nt, '<', 'new-text.txt' or die $!;
my %replace;
@replace{@lines} = <$nt>;
chomp for values %replace;

my $regex = join '|', map quotemeta, @lines;
open my $in, 'naive-file.txt' or die $!;
while (<$in>) {
    s/($regex)/$replace{$1}/;
    print;
}

Wenn einige der zu ersetzenden Zeichenfolgen Teilzeichenfolgen anderer zu ersetzender Zeichenfolgen sind, müssen Sie die Zeichenfolgen im regulären Ausdruck nach absteigender Länge sortieren, d. H.

my $regex = join '|', map quotemeta, sort { length $b <=> length $a } @lines;
Choroba
quelle
Was ist der Zweck von $regex, weil es hier nicht verwendet wird?
Rakesh Sharma
Guter Punkt, behoben.
Choroba
2
Ist das nicht Zeile für Zeile, während das OP eine große Menge von Bytes durch eine andere große Menge ersetzen wollte?
Roman Odaisky
Wie sollte man also die zu ersetzenden Blöcke anpassen? Sind sie durch zwei Zeilenumbrüche getrennt?
Choroba
4

Bash

Ersetzen Sie das erste Spiel:

target=$(cat naive-file.txt)
old=$(cat old-text.txt)
new=$(cat new-text.txt)
echo "${target/"$old"/"$new"}"

Ersetzen Sie alle Übereinstimmungen:

echo "${target//"$old"/"$new"}"

Anfangsübereinstimmung ersetzen:

echo "${target/#"$old"/"$new"}"

Endmatch ersetzen:

echo "${target/%"$old"/"$new"}"
dedowsdi
quelle
2

Hier ist der GNU awk-Einzeiler:

awk 'NR==FNR{old[++k]=$0}FILENAME=="new-text.txt"{new[FNR]=$0}
     FILENAME=="naive-file.txt"{for(i=1;i<k;i++)if(old[i]==$0)$0=new[i];print}'\
     old-text.txt new-text.txt naive-file.txt

Möglicherweise nicht für sehr große Dateien geeignet, da alle Muster zuerst in Arrays gespeichert werden.

Ausgabe:

Sed id ligula quis est convallis tempor.

This is the new text.

It could also have multiple lines and special characters like these \ { & %
etc...


Nunc aliquet, augue nec adipiscing interdum, lacus tellus malesuada massa, quis
varius mi purus non odio.
jimmij
quelle
Aus Effizienzgründen möchten Sie aus der Schleife ausbrechen, nachdem eine Übereinstimmung gefunden wurde? Das ändert natürlich auch das Verhalten des Programms, wenn der Inhalt alter und neuer Dateien so ist, dass sich eine Ersatzzeichenfolge später selbst für eine Ersetzung qualifizieren könnte
iruvar
@iruvar Ich habe aus der OP-Frage angenommen, dass die Muster einzigartig sind und ganze Linien bilden. Man kann die Schleife unterbrechen oder sogar (anstelle der Schleife) das erste Element aus dem Array nehmen und entfernen, wenn Muster in derselben Reihenfolge angezeigt werden - dies sollte bei großen Dateien viel schneller sein.
Jimmyij
0
$ perl -0777ne '
     $A[@ARGV] = $_;
     @ARGV and next;
     my($naive, $new, $old) = @A;
     while ( index($naive,$old,$p) > -1 ) {
        substr($naive, index($naive,$old,$p), length($old)) = $new;
        $p = index($naive,$old,length($old)) ;
      }
      print $naive;
' old.txt new.txt naive.txt
Rakesh Sharma
quelle