117 votes

Comment détecter si un fichier est binaire (non textuel) en Python ?

Comment puis-je savoir si un fichier est binaire (non textuel) en Python ?

J'effectue une recherche dans un grand ensemble de fichiers en Python, et je continue à obtenir des correspondances dans des fichiers binaires. Cela donne un aspect incroyablement désordonné à la sortie.

Je sais que je pourrais utiliser grep -I mais je fais plus avec les données que ce que grep permet.

Dans le passé, j'aurais simplement cherché les caractères supérieurs à 0x7f mais utf8 et autres, rendent cela impossible sur les systèmes modernes. Idéalement, la solution devrait être rapide.

0 votes

SI "dans le passé, j'aurais simplement cherché les caractères supérieurs à 0x7f", alors vous aviez l'habitude de travailler avec du texte ASCII brut, alors il n'y a toujours pas de problème puisque le texte ASCII codé en UTF-8 reste ASCII (c'est-à-dire pas d'octets > 127).

0 votes

@ : C'est vrai, mais il se trouve que je sais que certains des fichiers dont je m'occupe sont en utf8. Je voulais dire "utilisé pour" au sens général, pas au sens spécifique de ces fichiers :)

1 votes

Seulement avec des probabilités. Vous pouvez vérifier si : 1) le fichier contient \n 2) Quantité d'octets entre \n s est relativement petit (ceci n'est PAS fiable)l 3) le fichier ne contient pas d'octets dont la valeur est inférieure à celle du caractère "espace" (' ') de l'ASCCI - SAUF " \n " " \r " " \t " et des zéros.

70voto

J.F. Sebastian Points 102961

Encore une autre méthode basé sur le comportement du fichier(1) :

>>> textchars = bytearray({7,8,9,10,12,13,27} | set(range(0x20, 0x100)) - {0x7f})
>>> is_binary_string = lambda bytes: bool(bytes.translate(None, textchars))

Exemple :

>>> is_binary_string(open('/usr/bin/python', 'rb').read(1024))
True
>>> is_binary_string(open('/usr/bin/dh_python3', 'rb').read(1024))
False

0 votes

On peut obtenir à la fois des faux positifs et des faux négatifs, mais c'est tout de même une approche intelligente qui fonctionne pour la grande majorité des dossiers. +1.

2 votes

Il est intéressant de noter que file(1) lui-même exclut 0x7f de la considération, donc techniquement parlant vous devriez utiliser bytearray([7,8,9,10,12,13,27]) + bytearray(range(0x20, 0x7f)) + bytearray(range(0x80, 0x100)) à la place. Voir Python, file(1) - Pourquoi les nombres [7,8,9,10,12,13,27] et l'intervalle(0x20, 0x100) sont-ils utilisés pour déterminer le fichier texte ou binaire ? y github.com/file/file/blob/

1 votes

@MartijnPieters : merci. J'ai mis à jour la réponse pour exclure 0x7f ( DEL ) .

45voto

Gavin M. Roy Points 1501

Vous pouvez également utiliser le mimetypes module :

import mimetypes
...
mime = mimetypes.guess_type(file)

Il est assez facile de compiler une liste des types mime binaires. Par exemple, Apache est distribué avec un fichier mime.types que vous pouvez analyser en un ensemble de listes, binaire et texte, puis vérifier si le type mime se trouve dans votre liste texte ou binaire.

23 votes

Y a-t-il un moyen d'obtenir mimetypes pour utiliser le contenu d'un fichier plutôt que son seul nom ?

6 votes

@intuited Non, mais libmagic le fait. Utilisez-le via python-magie .

0 votes

Il y a une question similaire avec quelques bonnes réponses ici : stackoverflow.com/questions/1446549/ La réponse basée sur une recette d'activestate me semble bonne, elle autorise une petite proportion de caractères non imprimables (mais pas de \0 pour une raison quelconque).

8voto

Jorge Orpinel Points 109

Essayez ça :

def is_binary(filename):
    """Return true if the given filename is binary.
    @raise EnvironmentError: if the file does not exist or cannot be accessed.
    @attention: found @ http://bytes.com/topic/python/answers/21222-determine-file-type-binary-text on 6/08/2010
    @author: Trent Mick <TrentM@ActiveState.com>
    @author: Jorge Orpinel <jorge@orpinel.com>"""
    fin = open(filename, 'rb')
    try:
        CHUNKSIZE = 1024
        while 1:
            chunk = fin.read(CHUNKSIZE)
            if '\0' in chunk: # found null byte
                return True
            if len(chunk) < CHUNKSIZE:
                break # done
    # A-wooo! Mira, python no necesita el "except:". Achis... Que listo es.
    finally:
        fin.close()

    return False

10 votes

-1 définit "binaire" comme contenant un octet zéro. Classera les fichiers texte encodés en UTF-16 comme "binaires".

6 votes

@John Machin : Intéressant, git diff en fait fonctionne de cette façon et bien sûr, il détecte les fichiers UTF-16 comme binaires.

0 votes

Hunh. GNU diff fonctionne également de cette manière. Il a des problèmes similaires avec les fichiers UTF-16. file détecte correctement les mêmes fichiers en tant que texte UTF-16. Je n'ai pas vérifié grep Il détecte également les fichiers UTF-16 comme des fichiers binaires.

8voto

Shane C. Mason Points 4074

Si cela peut vous aider, de nombreux types binaires commencent par un chiffre magique. Voici une liste de signatures de fichiers.

0 votes

C'est à ça que sert libmagic. On peut y accéder en python via python-magie .

4 votes

Malheureusement, "ne commence pas par un nombre magique connu" n'est pas équivalent à "est un fichier texte".

6voto

Jacob Gabrielson Points 8800

Voici une suggestion qui utilise le système Unix fichier commandement :

import re
import subprocess

def istext(path):
    return (re.search(r':.* text',
                      subprocess.Popen(["file", '-L', path], 
                                       stdout=subprocess.PIPE).stdout.read())
            is not None)

Exemple d'utilisation :

\>>> istext('/etc/motd') 
True
>>> istext('/vmlinuz') 
False
>>> open('/tmp/japanese').read()
'\\xe3\\x81\\x93\\xe3\\x82\\x8c\\xe3\\x81\\xaf\\xe3\\x80\\x81\\xe3\\x81\\xbf\\xe3\\x81\\x9a\\xe3\\x81\\x8c\\xe3\\x82\\x81\\xe5\\xba\\xa7\\xe3\\x81\\xae\\xe6\\x99\\x82\\xe4\\xbb\\xa3\\xe3\\x81\\xae\\xe5\\xb9\\x95\\xe9\\x96\\x8b\\xe3\\x81\\x91\\xe3\\x80\\x82\\n'
>>> istext('/tmp/japanese') # works on UTF-8
True

Il présente l'inconvénient de ne pas être portable sous Windows (à moins que vous ne disposiez de quelque chose comme l'application file ), et de devoir lancer un processus externe pour chaque fichier, ce qui n'est pas toujours agréable.

0 votes

Cela a cassé mon script :( En enquêtant, j'ai découvert que certains conffiles sont décrits par file comme "Sendmail frozen configuration - version m"- remarquez l'absence de la chaîne "text". Peut-être utiliser file -i ?

2 votes

TypeError : Impossible d'utiliser un modèle de chaîne de caractères sur un objet de type octet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X