Jak się dowiedzieć, czy plik tekstowy jest podzbiorem innego pliku

12

Próbuję znaleźć sposób, aby ustalić, czy plik tekstowy jest podzbiorem innego ..

Na przykład:

foo
bar

jest podzbiorem

foo
bar
pluto

Podczas:

foo
pluto

i

foo
bar

nie są podzbiorem ...

Czy można to zrobić za pomocą polecenia?

Ta kontrola musi być kontrolą krzyżową i musi zwrócić:

file1 subset of file2 :    True
file2 subset of file1 :    True
otherwise             :    False
gc5
źródło
Potencjalnie bardziej wydajne rozwiązanie (jeśli pliki są również zamawiane): github.com/barrycarter/bcapps/blob/master/…
barrycarter

Odpowiedzi:

11

Jeżeli te są nazywane zawartość pliku file1, file2a file3w kolejności apearance następnie można to zrobić za pomocą następującego jednej liniowej:

 # 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
źródło
Dziękuję za twoją odpowiedź .. +1 .. Nie wiem, czy zaakceptuję moją odpowiedź, ponieważ twoja nie jest specyficzna dla Uniksa-Linuksa, a moja odpowiedź jest nieco szybsza, o ile ją przetestowałem .. co myślisz?
gc5
Witaj, są oczywiście inne rozwiązania z narzędziami bardziej uniksowymi. Ale wydaje się to dobrym zastosowaniem inoperatora Pythona .
Timo
Istnieje otoki wiersza poleceń Pythona, aby uczynić go bardziej uniksowym, z wbudowanym potokiem o nazwie pyp: code.google.com/p/pyp Myślę, że uczynienie tego rozwiązania bardziej uniksowym jest jak narzędzie do tworzenia linków .
IBr
3

Z 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

-0octaldefiniuje ogranicznik rekordu. Kiedy ta liczba ósemkowa jest większa niż 0377 (maksymalna wartość bajtu), oznacza to, że nie ma separatora, jest to równoważne z działaniem $/ = undef. W takim przypadku <>zwraca pełną zawartość jednego pliku, to jest tryb slurp .

Gdy mamy zawartość plików na dwie części $hi $nzmienne, możemy użyć, index()aby ustalić, czy jeden znajduje się w drugim.

Oznacza to jednak, że całe pliki są przechowywane w pamięci, co oznacza, że ​​metoda nie będzie działać w przypadku bardzo dużych plików.

W przypadku plików mmappable (zwykle obejmuje zwykłe pliki i najbardziej widoczne pliki, takie jak urządzenia blokowe), które można obejść za pomocą mmap()plików, takich jak Sys::Mmapmoduł perla:

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
źródło
2

Znalazłem rozwiązanie dzięki temu pytaniu

Zasadniczo testuję dwa pliki a.txti za b.txtpomocą tego skryptu:

#!/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

Jeśli jeden jest podzbiorem drugiego, skrypt powraca 0w Trueprzeciwnym wypadku 1.

gc5
źródło
Co robi% L? Ten skrypt wydaje się nie działać, a ja próbuję go debugować ...
Alex
Właściwie nie pamiętam znaczenia %L, to było trzy lata temu. Od man diff(aktualna wersja) %Loznacza „zawartość linii”.
gc5
% L drukuje zawartość „nowej” linii. IOW, nie drukuj niczego dla niezmienionych linii lub starych linii, ale drukuj zawartość linii dla nowych linii.
PLG
Ten skrypt działa dla mnie od razu po wyjęciu z pudełka!
PLG,
2

Jeśli f1 jest podzbiorem f2, to f1 - f2 jest pustym zestawem. Na tej podstawie możemy napisać funkcję is_subset i funkcję pochodną z niej. Zgodnie z Ustaw różnicę między 2 plikami tekstowymi


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

  Jeśli [ ! -f $ f1_sorted]; następnie
    kot 1 $ | sortuj | uniq> $ f1_sorted
  fi

  Jeśli [ ! -f $ f2_sorted]; następnie
    kot 2 $ | sortuj | 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 $
  kot „$ 1.sortowane” „$ 2.sortowane” | sortuj | uniq
  remove_sorted_files 1 USD 2 USD
}

set_diff () {
  sort_files 1 $ 2 $
  kot „$ 1.sortowane” „$ 2.sortowane” „$ 2.sortowane” | sortuj | uniq -u
  remove_sorted_files 1 USD 2 USD
}

rset_diff () {
  sort_files 1 $ 2 $
  kot „$ 1.sortowane” „$ 2.sortowane” „$ 1.sortowane” | sortuj | uniq -u
  remove_sorted_files 1 USD 2 USD
}

is_subset () {
  sort_files 1 $ 2 $
  wyjście = $ (set_diff $ 1 $ 2)
  remove_sorted_files 1 USD 2 USD

  if [-z $ wyjście]; następnie
    zwróć 0
  jeszcze
    zwrot 1
  fi

}

Saurabh Hirani
źródło
Czy ten skrypt powinien zaczynać się od #!/bin/bash?
Alex
2

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

Comm porównuje dwa posortowane pliki linia po linii. Można go uruchomić w taki sposób, aby wyświetlał wiersze pojawiające się tylko w pierwszym określonym pliku. Jeśli pierwszy plik jest podzbiorem drugiego, wówczas wszystkie wiersze w 1. pliku pojawiają się również w 2., więc nie są generowane żadne dane wyjściowe:

$ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1
# comm returns no output if subset ⊆ set
# comm outputs something if subset ⊊ set
Alec
źródło