Come sapere se un file di testo è un sottoinsieme di un altro


12

Sto cercando di trovare un modo per determinare se un file di testo è un sottoinsieme di un altro ..

Per esempio:

foo
bar

è un sottoinsieme di

foo
bar
pluto

Mentre:

foo
pluto

e

foo
bar

non sono un sottoinsieme l'uno dell'altro ...

C'è un modo per farlo con un comando?

Questo controllo deve essere un controllo incrociato e deve restituire:

file1 subset of file2 :    True
file2 subset of file1 :    True
otherwise             :    False

Soluzione potenzialmente più efficiente (se vengono ordinati anche file): github.com/barrycarter/bcapps/blob/master/…
barrycarter il

Risposte:


11

Se quei contenuto dei file sono chiamati file1, file2e file3in ordine di apearance allora si può fare con il seguente one-liner:

 # 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

Grazie per la tua risposta .. +1 .. Non so se accettare la mia risposta perché la tua non è specifica per unix-linux e la mia risposta è un po 'più veloce, per quanto l'ho provata .. che ne pensi?
gc5,

Benvenuta, ci sono ovviamente altre soluzioni con strumenti specifici più unix. Ma questo sembra un buon uso dell'operatore di Python in.
Timo,

Esiste un wrapper di riga di comando python per renderlo più unix simile, con piping integrato, chiamato pyp: code.google.com/p/pyp Penso che sia banale rendere questa soluzione più unix come uno strumento di linea.
IBr

3

Con 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

-0octaldefinisce il delimitatore del record. Quando quel numero ottale è maggiore di 0377 (il valore massimo di byte), significa che non c'è delimitatore, è equivalente a fare $/ = undef. In tal caso, <>restituisce l'intero contenuto di un singolo file, questa è la modalità slurp .

Una volta che abbiamo il contenuto dei file in due $he $nvariabili, possiamo usare index()per determinare se uno si trova nell'altro.

Ciò significa tuttavia che tutti i file sono archiviati in memoria, il che significa che il metodo non funzionerà per file molto grandi.

Per i file mmappable (di solito include file regolari e file più ricercabili come i dispositivi a blocchi), che possono essere aggirati usando mmap()i file, come con il Sys::Mmapmodulo perl:

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

Ho trovato una soluzione grazie a questa domanda

Fondamentalmente sto testando due file a.txte b.txtcon questo script:

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

Se uno è un sottoinsieme dell'altro, lo script ritorna in 0caso Truecontrario 1.


Cosa fa% L? Questa sceneggiatura non sembra funzionare e sto cercando di eseguire il debug ...
Alex,

In realtà non ricordo il significato di %L, è stato tre anni fa. Da man diff(versione corrente) %Lsignifica "contenuto della riga".
gc5,

% L stampa il contenuto della "nuova" riga. IOW, non stampare nulla per le righe invariate o le vecchie righe, ma stampa il contenuto della riga per le nuove righe.
PLG,

Questo script funziona per me, fuori dalla scatola!
PLG,

2

Se f1 è un sottoinsieme di f2, allora f1 - f2 è un set vuoto. Partendo da ciò possiamo scrivere una funzione is_subset e una funzione derivata da essa. Come da Imposta la differenza tra 2 file di testo


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

  Se [ ! -f $ f1_sorted]; poi
    gatto $ 1 | ordina | uniq> $ f1_sorted
  fi

  Se [ ! -f $ f2_sorted]; poi
    gatto $ 2 | ordina | 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
  gatto "$ 1.sorted" "$ 2.sorted" | ordina | uniq
  remove_sorted_files $ 1 $ 2
}

set_diff () {
  sort_files $ 1 $ 2
  gatto "$ 1.sorted" "$ 2.sorted" "$ 2.sorted" | ordina | uniq -u
  remove_sorted_files $ 1 $ 2
}

rset_diff () {
  sort_files $ 1 $ 2
  gatto "$ 1.sorted" "$ 2.sorted" "$ 1.sorted" | ordina | 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]; poi
    ritorna 0
  altro
    ritorno 1
  fi

}


Questo script dovrebbe iniziare con #!/bin/bash?
Alex,

2

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

Comm confronta due file ordinati riga per riga. Può essere eseguito in modo tale da generare righe che compaiono solo nel primo file specificato. Se il primo file è un sottoinsieme del secondo, anche tutte le righe del 1o file appaiono nel 2o, quindi non viene prodotto alcun output:

$ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1
# comm returns no output if subset ⊆ set
# comm outputs something if subset ⊊ set
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.