2009-02-19 18 views
52

Informalmente, molti di noi comprendono che esistono file "binari" (file oggetto, immagini, filmati, file eseguibili, formati di documenti proprietari, ecc.) E file "di testo" (codice sorgente, file XML, file HTML, e-mail, ecc.) .Come faccio a distinguere tra file "binari" e "testo"?

In generale, è necessario conoscere il contenuto di un file per poter fare qualcosa di utile con esso e formare quel punto di vista se la codifica è 'binaria' o 'testo', non importa . E naturalmente i file memorizzano solo byte di dati in modo che siano tutti "binari" e "testo" non significa nulla senza conoscere la codifica. Eppure, è ancora utile parlare di file "binari" e "di testo", ma per evitare di offendere qualcuno con questa definizione imprecisa, continuerò a usare virgolette "spaventose".

Tuttavia, ci sono vari strumenti che funzionano su una vasta gamma di file, e in termini pratici, si vuole fare qualcosa di diverso in base al fatto che il file sia 'testo' o 'binario'. Un esempio di questo è uno strumento che emette i dati sulla console. Il semplice 'testo' apparirà bene ed è utile. I dati "binari" incasinano il tuo terminale e generalmente non sono utili da guardare. GNU grep utilizza almeno questa distinzione quando determina se deve generare corrispondenze con la console.

Quindi, la domanda è, come si dice se un file è "testo" o "binario"? E per restringere è ulteriormente, come si fa a dire su un Linux come file system? Non sono a conoscenza di alcun metadato del filesystem che indica il "tipo" di un file, quindi la domanda diventa ulteriormente, ispezionando il contenuto di un file, come faccio a sapere se è "testo" o "binario"? E per semplicità, consente di limitare 'testo' per significare caratteri che sono stampabili sulla console dell'utente. E in particolare come vorresti implementare lo ? (Pensavo che questo fosse implicito in questo sito, ma immagino che sia utile, in generale, essere puntato sul codice esistente che fa questo, avrei dovuto specificare), non sto veramente dopo quali programmi esistenti posso usare per fare Questo.

risposta

11

Il nostro software legge un numero di formati di file binari e file di testo.

Prima vediamo i primi pochi byte per uno magic number che riconosciamo. Se non riconosciamo il numero magico di nessuno dei tipi binari che leggiamo, allora guardiamo fino ai primi 2K byte del file per vedere se sembra essere un UTF-8, UTF-16 o un file di testo codificato nell'attuale code page del sistema operativo host. Se non supera nessuno di questi test, assumiamo che non si tratti di un file che possiamo gestire e di generare un'eccezione appropriata.

+17

non si dice cosa sia il "nostro software", rallentando l'analisi umana. – vwvan

4

Bene, se stai solo ispezionando l'intero file, verifica se ogni carattere è stampabile con isprint(c). Diventa un po 'più complicato per Unicode.

Per distinguere un file di testo Unicode, MSDN offers some great advice as to what to do.

L'essenza di esso è di ispezionare prima fino ai primi quattro byte:

EF BB BF  UTF-8 
FF FE  UTF-16, little endian 
FE FF  UTF-16, big endian 
FF FE 00 00 UTF-32, little endian 
00 00 FE FF UTF-32, big-endian 

Che vi dirà la codifica. Quindi, vorresti usare iswprint(c) per il resto dei caratteri nel file di testo. Per UTF-8 e UTF-16, è necessario analizzare i dati manualmente poiché un singolo carattere può essere rappresentato da un numero variabile di byte. Inoltre, se sei davvero anale, ti consigliamo di utilizzare la variante locale di iswprint se è disponibile sulla tua piattaforma.

+0

Funziona solo per i file che utilizzano questa regola. –

+0

Beh, se non segue quelle regole, in realtà non è un file di testo. Tranne per mbcs, ma questa è una storia completamente diversa. – MSN

+3

La preimpostazione di un file BOM in UTF-8 non è incoraggiata dallo standard Unicode, ed è un peccato che non lo vietino a titolo definitivo. Inoltre, quegli altri formati non ne hanno necessariamente uno. – Deduplicator

2

La maggior parte dei programmi che cercano di capire la differenza utilizzare un euristica, come ad esempio esaminando i primi n byte del file e vedere se quelli byte tutti qualificarsi come 'testo' o no (cioè, fanno cadono tutti all'interno della gamma di caratteri ASCII stampabili). Per una distinzione più precisa c'è sempre il comando 'file' su sistemi simil-UNIX.

60

È possibile utilizzare il comando file. Fa un sacco di test sul file (man file) per decidere se è binario o di testo. Puoi guardare/prendere in prestito il suo codice sorgente se hai bisogno di farlo da C.

file README 
README: ASCII English text, with very long lines 

file /bin/bash 
/bin/bash: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped 
+0

+1 Se si tratta di un sistema Linux, il file avrà un'euristica molto migliore di qualsiasi cosa tu possa costruire da solo. –

+0

Sì, se il file è disponibile, sarà lo strumento migliore per il lavoro. Nessuna domanda! Anche il 'file -I' è un trucco accurato. Non avevo pensato di sborsare per il mio particolare problema, tuttavia non penso che potrei fare i conti con le prestazioni generali. Grazie! – benno

10

È possibile determinare il MIME type del file con

file --mime 

La stenografia è file -i su Linux e file -I (capitale i) su MacOS (vedi commenti).

Se inizia con text/, è testo, altrimenti binario. L'unica eccezione sono le applicazioni XML. È possibile abbinare quelli cercando +xml alla fine del tipo di file.

+0

Penso che dovrebbe essere "file -I" (maiuscolo). Almeno secondo i miei test e la pagina man. – benno

+1

Ho appena cercato, la minuscola è corretta in Debian e gentoo Linux. Il loro file è ftp://ftp.astron.com/pub/file/file-5.00.tar.gz (o una versione diversa). -I (superiore) è un'opzione in nessuno dei due. – phihag

+0

Huh, strano. La versione su OS X (4.17) usa -I (superiore) e quella sulle mie scatole Linux (4.24) usa -i (inferiore). Che bizzarro! Mi chiedo se si tratta di un OS X-ismo, o semplicemente gli autori hanno cambiato l'interfaccia tra il rilascio dei punti. – benno

1

Un semplice controllo è se ha caratteri \0. I file di testo non li hanno.

+9

a meno che non sia utf-16 o utf32. allora c'è molto. – Breton

1

Come già affermato * i sistemi operativi nix hanno questa capacità all'interno del comando file. Questo comando utilizza un file di configurazione che definisce i numeri magici contenuti in molte strutture di file popolari.

Questo file, chiamato magic è stato storicamente memorizzato in/etc, sebbene questo possa essere in/usr/share su alcune distribuzioni. Il file magico definisce gli offset dei valori noti all'interno del file e può quindi esaminare queste posizioni per determinare il tipo di file.

La struttura e la descrizione del file magia si possono trovare consultando la pagina di manuale relativa (uomo magico)

Per quanto riguarda l'implementazione, così che possono essere trovati all'interno di file.c sé, tuttavia la quota di competenza del file il comando che determina se è leggibile o meno il testo è il seguente

/* Make sure we are dealing with ascii text before looking for tokens */ 
    for (i = 0; i < nbytes - 1; i++) { 
     if (!isascii(buf[i]) || 
      (iscntrl(buf[i]) && !isspace(buf[i]) && 
      buf[i] != '\b' && buf[i] != '\032' && buf[i] != '\033' 
      ) 
      ) 
      return 0; /* not all ASCII */ 
    } 
3

Perl ha un euristico decente. Utilizzare l'operatore -B per eseguire il test di binario (e il suo contrario, -T per verificare il testo). Qui di shell un one-liner per elencare i file di testo:

$ find . -type f -print0 | perl -0nE 'say if -f and -s _ and -T _' 

(Si noti che queste sottolineature senza un dollaro precedente sono corrette (RTFM).)

2

Il suo vecchio argomento, ma forse qualcuno troverà questo utile . Se si deve decidere in uno script se qualcosa è un file allora si può semplicemente fare come questo:

if file -i $1 | grep -q text; 
then 
. 
. 
fi 

In questo modo ottenere il tipo di file, e con un grep silenziosa si può decidere se un testo.

+0

osx ha due varianti per questo: minuscolo -i stamperà il tipo senza classificazione (ad es. File, directory); maiuscolo -I stamperò la classificazione, in modo simile a quanto ci si aspetterebbe da un sistema Linux. Si vorrà usare maiuscole -I perché funzioni su quella piattaforma – verboze

0

È possibile utilizzare libmagic che è una versione di libreria della riga di comando di Unix file.

ci sono wrapper per molte lingue:

0

Per lis i nomi dei file di testo t in corrente dir/subdirs:

$ grep -rIl '' 

Binaries:

$ grep -rIL '' 

per controllare particolare file, modificare leggermente comando:

$ grep -qI '' FILE 

poi, stato d'uscita '0' sarebbe significa che il file è un testo; '1' - binario. Potrebbe verificare:

$ echo $?

+0

Questa è una soluzione funzionante. Per favore, spiega la ragione del downvote, forse dovrei migliorare la risposta in qualche modo – bam

+0

L'ho provato su file generati da dd e da nano. Il tuo metodo funziona alla grande. Mi interessa anche perché ci sono stati voti bassi. – Daniel