Wie erkennt man, ob eine Textdatei eine Teilmenge einer anderen ist?

12

Ich versuche herauszufinden, ob eine Textdatei eine Teilmenge einer anderen ist.

Beispielsweise:

foo
bar

ist eine Teilmenge von

foo
bar
pluto

Während:

foo
pluto

und

foo
bar

sind keine Untergruppe voneinander ...

Gibt es eine Möglichkeit, dies mit einem Befehl zu tun?

Diese Prüfung muss eine Gegenprüfung sein und Folgendes zurückgeben:

file1 subset of file2 :    True
file2 subset of file1 :    True
otherwise             :    False
gc5
quelle
Potenziell effizientere Lösung (wenn Dateien
mitbestellt werden

Antworten:

11

Wenn diese Dateiinhalte aufgerufen file1werden file2und file3in der Reihenfolge ihres Erscheinens, können Sie dies mit dem folgenden Einzeiler tun:

 # python -c "x=open('file1').read(); y=open('file2').read(); print x in y or y in x"
 True
 # python -c "x=open('file2').read(); y=open('file1').read(); print x in y or y in x"
 True
 # python -c "x=open('file1').read(); y=open('file3').read(); print x in y or y in x"
 False
Timo
quelle
Danke für deine Antwort. +1. Ich weiß nicht, ob ich meine Antwort akzeptiere, weil deine nicht für Unix-Linux spezifisch ist und meine Antwort ein bisschen schneller ist, soweit ich sie getestet habe. Was denkst du?
GC5
Gerne gibt es natürlich auch andere Lösungen mit unixspezifischeren Tools. Dies scheint jedoch eine gute Verwendung des Python- inOperators zu sein.
Timo
Es gibt einen Python-Befehlszeilen-Wrapper, der es einfacher macht , mit eingebauten Piping-Funktionen namens pyp: code.google.com/p/pyp .
14.
3

Mit perl:

if perl -0777 -e '$n = <>; $h = <>; exit(index($h,$n)<0)' needle.txt haystack.txt
then echo needle.txt is found in haystack.txt
fi

-0octalDefiniert den Datensatzbegrenzer. Wenn diese Oktalzahl größer als 0377 (der maximale Bytewert) ist, bedeutet dies, dass es keinen Begrenzer gibt. Dies entspricht dem Vorgang $/ = undef. In diesem Fall wird <>der vollständige Inhalt einer einzelnen Datei zurückgegeben. Dies ist der Slurp-Modus .

Sobald wir den Inhalt der Dateien in zwei $hund $nVariablen haben, können wir index()feststellen, ob eine in der anderen gefunden wird.

Dies bedeutet jedoch, dass die gesamten Dateien im Speicher abgelegt werden, was bedeutet, dass die Methode bei sehr großen Dateien nicht funktioniert.

Für mmappable-Dateien (enthält normalerweise reguläre Dateien und am meisten durchsuchbare Dateien wie Block-Geräte), die durch Verwendung mmap()der Dateien wie mit dem Sys::MmapPerl-Modul umgangen werden können:

if 
  perl -MSys::Mmap -le '
    open N, "<", $ARGV[0] || die "$ARGV[0]: $!";
    open H, "<", $ARGV[1] || die "$ARGV[1]: $!";
    mmap($n, 0, PROT_READ, MAP_SHARED, N);
    mmap($h, 0, PROT_READ, MAP_SHARED, H);
    exit (index($h, $n) < 0)' needle.txt haystack.txt
then
  echo needle.txt is found in haystack.txt
fi
Stéphane Chazelas
quelle
2

Dank dieser Frage habe ich eine Lösung gefunden

Grundsätzlich teste ich zwei Dateien a.txtund b.txtmit diesem Skript:

#!/bin/bash

first_cmp=$(diff --unchanged-line-format= --old-line-format= --new-line-format='%L' "$1" "$2" | wc -l)
second_cmp=$(diff --unchanged-line-format= --old-line-format= --new-line-format='%L' "$2" "$1" | wc -l)

if [ "$first_cmp" -eq "0" -o "$second_cmp" -eq "0" ]
then
    echo "Subset"
    exit 0
else
    echo "Not subset"
    exit 1
fi

Wenn man von der anderen Skript Rückkehr Teilmenge 0für Truesonst 1.

gc5
quelle
Was macht% L? Dieses Skript scheint nicht zu funktionieren, und ich versuche, es zu debuggen ...
Alex
Ich erinnere mich eigentlich nicht an die Bedeutung von %L, es war vor drei Jahren. From man diff(aktuelle Version) %Lbedeutet "Zeileninhalt".
GC5
% L druckt den Inhalt der "neuen" Zeile. IOW, drucke nichts für unveränderte oder alte Zeilen, sondern den Inhalt der Zeile für neue Zeilen.
PLG
Dieses Skript funktioniert bei mir sofort!
PLG
2

Wenn f1 eine Teilmenge von f2 ist, dann ist f1 - f2 eine leere Menge. Darauf aufbauend können wir eine is_subset-Funktion und eine davon abgeleitete Funktion schreiben. Gemäß Satzunterschied zwischen 2 Textdateien


sort_files () {
  f1_sorted = "$ 1.sorted"
  f2_sorted = "$ 2.sorted"

  wenn [ ! -f $ f1_sorted]; dann
    Katze $ 1 | sortieren | uniq> $ f1_sorted
  fi

  wenn [ ! -f $ f2_sorted]; dann
    Katze $ 2 | sortieren | uniq> $ f2_sorted
  fi
}

remove_sorted_files () {
  f1_sorted = "$ 1.sorted"
  f2_sorted = "$ 2.sorted"
  rm -f $ f1_sorted
  rm -f $ f2_sorted
}

set_union () {
  sort_files $ 1 $ 2
  Katze "$ 1.sortiert" "$ 2.sortiert" | sortieren | uniq
  remove_sorted_files $ 1 $ 2
}

set_diff () {
  sort_files $ 1 $ 2
  Katze "$ 1.sortiert" "$ 2.sortiert" "$ 2.sortiert" | sortieren | uniq -u
  remove_sorted_files $ 1 $ 2
}

rset_diff () {
  sort_files $ 1 $ 2
  Katze "$ 1.sortiert" "$ 2.sortiert" "$ 1.sortiert" | sortieren | uniq -u
  remove_sorted_files $ 1 $ 2
}

is_subset () {
  sort_files $ 1 $ 2
  output = $ (set_diff $ 1 $ 2)
  remove_sorted_files $ 1 $ 2

  if [-z $ output]; dann
    0 zurückgeben
  sonst
    return 1
  fi

}

Saurabh Hirani
quelle
Sollte dieses Skript mit beginnen #!/bin/bash?
Alex
2

Von http://www.catonmat.net/blog/set-operations-in-unix-shell/ :

Comm vergleicht zwei sortierte Dateien zeilenweise. Es kann so ausgeführt werden, dass es Zeilen ausgibt, die nur in der ersten angegebenen Datei erscheinen. Wenn die erste Datei eine Teilmenge der zweiten ist, erscheinen alle Zeilen in der ersten Datei auch in der zweiten, sodass keine Ausgabe erfolgt:

$ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1
# comm returns no output if subset ⊆ set
# comm outputs something if subset ⊊ set
Alec
quelle