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

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

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

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.


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

}


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.