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.