¿Cómo puedo saber si un archivo es binario (no de texto) en python?

Estoy buscando a través de un gran conjunto de archivos en python, y seguir recibiendo los partidos en archivos binarios. Esto hace que la salida de aspecto increíblemente desordenado.

Sé que podría utilizar grep -I, pero me estoy haciendo más con los datos de lo que grep permite.

En el pasado, yo habría buscado caracteres mayores que 0x7f, pero utf8 y similares, hacen que imposible en los sistemas modernos. Lo ideal es que la solución sería rápido, pero ninguna solución.

  • SI «en el pasado, yo habría buscado caracteres mayor que 0x7f», a CONTINUACIÓN, se utiliza para trabajar con texto ASCII sin formato, ENTONCES todavía no hay problema ya que de texto ASCII codificado como UTF-8 sigue siendo ASCII (es decir, no bytes > 127).
  • Cierto, pero me he enterado de que algunos de los archivos que estoy viendo son utf8. Quiero decir en el sentido general, no en el sentido específico de estos archivos. 🙂
  • Sólo con la probabilidad. Usted puede comprobar si: 1) el archivo contiene \n 2) Cantidad de bytes entre \n es relativamente pequeño (esto NO es confiable)l 3) el archivo no bytes con valor menor que el valor de ASCCI carácter de «espacio» ( » ) – EXCEPTO «\n» «\r» «\t» y ceros.
  • La estrategia que grep misma que se utiliza para identificar los archivos binarios es similar a la publicada por Jorge Orpinel below. A menos que establezca la -z opción, se acaba de escanear en busca de un carácter nulo ("\000") en el archivo. Con -z, se busca "\200". Los interesados y/o escéptico puede comprobar la línea de 1126 de grep.c. Lo siento, no pude encontrar una página web con el código fuente, pero, por supuesto, usted puede conseguir a partir de gnu.org o a través de un distribución.
  • P. S. Como se ha mencionado en los comentarios del hilo de Jorge del post, esta estrategia va a dar falsos positivos para los archivos que contienen, por ejemplo, texto UTF-16. No obstante, ambos git diff y GNU diff también utilizar la misma estrategia. No estoy seguro de si es tan frecuente porque es mucho más rápido y más fácil que la alternativa, o si es sólo a causa de la relativa rareza de archivos UTF-16 en los sistemas que tienden a tener estos utils instalado.
  • El uso de una biblioteca (véase mi respuesta a continuación).

InformationsquelleAutor grieve | 2009-05-22

19 Comentarios

  1. 37

    También puede utilizar el mimetypes módulo:

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

    Es bastante fácil de compilar una lista de los binarios de los tipos mime. Por ejemplo Apache distribuye con un mimo.los tipos de archivo que se podría analizar en un conjunto de listas, binarios y de texto y, a continuación, comprobar para ver si el mime está en el texto o binario lista.

    • Es allí una manera de conseguir mimetypes para utilizar el contenido de un archivo en lugar de sólo su nombre?
    • No, pero libmagic hace eso. Uso a través de python-magia.
    • Hay una pregunta similar, con algunas buenas respuestas aquí: stackoverflow.com/questions/1446549/… La respuesta basada en una de activestate receta se ve bien para mí, esto permite que una pequeña proporción de caracteres no imprimibles (pero no \0, por alguna razón).
    • Esto no es una gran respuesta, sólo porque el mimetypes módulo no es bueno para todos los archivos. Estoy buscando a un archivo de sistema que file informes como «UTF-8 Unicode texto, con las líneas muy largas», pero mimetypes.gest_type() devolverá (None, None). También, Apache mimetype lista es una lista blanca/subconjunto. Es de ninguna manera una lista completa de los mimetypes. No se puede utilizar para clasificar todos los archivos como texto o sin texto.
    • guess_types se basa en la extensión de nombre de archivo, no el contenido real como el comando Unix «archivo» iba a hacer.
    • \0 (null) auto falla porque no debe nunca ser un valor nulo en un archivo de texto. La mayoría de los editores de texto ver que y que es donde está el archivo de texto se considera a la final.
    • Puedo confirmar, guess_type se basa en la extensión del archivo. También, en el código de ejemplo, file es en realidad una cadena.

  2. 54

    Otro método basado en el archivo(1) comportamiento:

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

    Ejemplo:

    >>> is_binary_string(open('/usr/bin/python', 'rb').read(1024))
    True
    >>> is_binary_string(open('/usr/bin/dh_python3', 'rb').read(1024))
    False
    • Puede obtener tanto de falsos positivos y falsos negativos, pero todavía es un acercamiento inteligente que funciona para la gran mayoría de los archivos. +1.
    • Curiosamente, archivo(1) sí excluye a 0x7f de la consideración como bien, así que, técnicamente hablando, usted debe utilizar bytearray([7,8,9,10,12,13,27]) + bytearray(range(0x20, 0x7f)) + bytearray(range(0x80, 0x100)) lugar. Consulte Python, archivo(1) – ¿por Qué son los números [7,8,9,10,12,13,27] y el rango(0x20, 0x100) utilizado para la determinación de texto vs archivo binario y github.com/file/file/blob/…
    • gracias. He actualizado la respuesta a excluir 0x7f (DEL) .
    • Solución agradable con los juegos. 🙂
    • ¿Por qué excluir 11 o VT? En la tabla 11 se considera texto ASCII sin formato, y este es el vertical tab.
    • buena captura. Desde el file(1) enlace: «yo excluir de tabulación vertical porque nunca parece ser utilizado en el texto real.» Este comportamiento ha cambiado entre los diferentes file(1) versiones (tal vez, el enlace debe apuntar a una versión anterior). El método es sólo una heurística, utilizar lo que funciona mejor en su caso.
    • Qué Python garantía de que el archivo se cierra de inmediato si usted no usa un with instrucción para leer los 1024 bytes?
    • para asegurarse de que se cierra un archivo, utilice el with-declaración o llame .close() método explícitamente.
    • Yo sólo traigo a colación porque no hacer ninguna de esas cosas en esta respuesta.
    • es sólo un REPL ejemplo. Estoy seguro de que los archivos que desea comprobar, no son llamados /usr/bin/python literalmente demasiado.
    • los bytes no es de str.

  3. 10

    Intente esto:

    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 <[email protected]>
        @author: Jorge Orpinel <[email protected]>"""
        fin = open(filename, 'rb')
        try:
            CHUNKSIZE = 1024
            while 1:
                chunk = fin.read(CHUNKSIZE)
                if '
    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 <[email protected]>
    @author: Jorge Orpinel <[email protected]>"""
    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
    '
    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
    • -1 define «binario», ya que contiene un byte cero. Se clasifican en UTF-16 archivos de texto codificados como «binario».
    • Gracias @Juan Machin
    • Machin: Curiosamente, git diff en realidad funciona de esta manera, y, por supuesto, se detecta archivos UTF-16 como binario.
    • ¿Eh.. GNU diff también funciona de esta manera. Tiene problemas similares con los archivos UTF-16. file no detectar correctamente los mismos archivos como UTF-16 de texto. No he comprobado grep ‘s código, pero también detecta los archivos UTF-16 como binario.
    • +1 @Juan Machin: utf-16 es un personaje de datos de acuerdo a la file(1) que no es seguro para imprimir sin necesidad de conversión por lo que este método es apropiado en este caso.
    • -1 – no creo que ‘contiene un byte cero’ es una prueba adecuado para el binario vs texto, por ejemplo, puedo crear un archivo que contiene todos los 0x01 bytes o repetir 0xDEADBEEF, pero no es un archivo de texto. La respuesta basada en el archivo(1) es mejor.

  4. 10

    Si usted está usando python3 con utf-8 que es sencillo, sólo tienes que abrir el archivo en modo texto y detener el procesamiento de si usted consigue un UnicodeDecodeError. Python3 va a utilizar unicode cuando el manejo de archivos en modo texto (y bytearray en modo binario) – si la codificación no puede decodificar archivos arbitrarios es muy probable que usted obtendrá UnicodeDecodeError.

    Ejemplo:

    try:
        with open(filename, "r") as f:
            for l in f:
                 process_line(l)
    except UnicodeDecodeError:
        pass # Fond non-text data
  5. 8

    Si ayuda, muchos tipos binarios de comenzar con los números de magia. Aquí está una lista de firmas de archivo.

    • Que es lo que libmagic es para. Se puede acceder en python a través de python-magia.
    • Por desgracia, «no comienza con un conocido número mágico» no es equivalente a «es un archivo de texto».
  6. 6

    Aquí es una sugerencia que utiliza Unix archivo comando:

    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)

    Ejemplo de uso:

    >>> istext('/etc/motd') 
    Cierto 
    >>> istext('/vmlinuz') 
    Falso 
    >>> open('/tmp/japonés').leer() 
    '\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/japonés') # obras en UTF-8 
    Cierto 
    

    Tiene las desventajas de no ser portable para Windows (a menos que haya algo así como el file mando allí), y tener para generar un proceso externo para cada archivo, que puede no ser aceptable.

    • Esto rompió mi script 🙁 Investigando, me enteré de que algunos conffiles son descritos por file como «Sendmail congelados de configuración de la versión m»—aviso de la ausencia de la «cadena de texto». Tal vez el uso de file -i?
    • TypeError: no se puede utilizar un patrón de cadena en bytes-como objeto
  7. 5

    Uso binaryornot biblioteca (GitHub).

    Es muy simple y se basa en el código que se encuentra en esta pregunta de stackoverflow.

    Realidad se puede escribir esto en 2 líneas de código, sin embargo, este paquete te ahorra tener que escribir y probar exhaustivamente esas 2 líneas de código con todo tipo de extraños tipos de archivo, de la cruz-plataforma.

  8. 4

    Por lo general, usted tiene que adivinar.

    Usted puede buscar en las extensiones como uno de los motivos, si los archivos tienen ellos.

    También puede reconocer saber formatos binarios, y hacer caso omiso de los.

    De lo contrario, consulte ¿cuál es la proporción de ASCII no imprimibles de bytes que tiene y tomar una conjetura de que.

    También puede tratar de decodificación de la codificación UTF-8 y ver si se produce sensible de salida.

  9. 3

    Una solución más corto, con un UTF-16 advertencia:

    def is_binary(filename):
        """ 
        Return true if the given filename appears to be binary.
        File is considered to be binary if it contains a NULL byte.
        FIXME: This approach incorrectly reports UTF-16 as binary.
        """
        with open(filename, 'rb') as f:
            for block in f:
                if b'
    def is_binary(filename):
    """ 
    Return true if the given filename appears to be binary.
    File is considered to be binary if it contains a NULL byte.
    FIXME: This approach incorrectly reports UTF-16 as binary.
    """
    with open(filename, 'rb') as f:
    for block in f:
    if b'\0' in block:
    return True
    return False
    '
    in block: return True return False
    • nota: for line in file puede consumir una cantidad ilimitada de memoria hasta b'\n' se encuentra
    • a @Comunidad: ".read()" devuelve un bytestring aquí que está iterable (que los rendimientos de individuo bytes).
  10. 2

    Aquí una función que comprueba en primer lugar si el archivo comienza con una lista de materiales y si no busca un byte cero dentro de la inicial 8192 bytes:

    import codecs
    
    
    #: BOMs to indicate that a file is a text file even if it contains zero bytes.
    _TEXT_BOMS = (
        codecs.BOM_UTF16_BE,
        codecs.BOM_UTF16_LE,
        codecs.BOM_UTF32_BE,
        codecs.BOM_UTF32_LE,
        codecs.BOM_UTF8,
    )
    
    
    def is_binary_file(source_path):
        with open(source_path, 'rb') as source_file:
            initial_bytes = source_file.read(8192)
        return not any(initial_bytes.startswith(bom) for bom in _TEXT_BOMS) \
               and b'
    import codecs
    #: BOMs to indicate that a file is a text file even if it contains zero bytes.
    _TEXT_BOMS = (
    codecs.BOM_UTF16_BE,
    codecs.BOM_UTF16_LE,
    codecs.BOM_UTF32_BE,
    codecs.BOM_UTF32_LE,
    codecs.BOM_UTF8,
    )
    def is_binary_file(source_path):
    with open(source_path, 'rb') as source_file:
    initial_bytes = source_file.read(8192)
    return not any(initial_bytes.startswith(bom) for bom in _TEXT_BOMS) \
    and b'\0' in initial_bytes
    '
    in initial_bytes

    Técnicamente el cheque para el UTF-8 lista de materiales es innecesario, ya que no debe contener cero bytes para todos los fines prácticos. Pero como es un tipo muy común de codificación es más rápido para comprobar la lista de materiales en el principio en lugar de escanear todos los 8192 bytes de 0.

  11. 2

    Podemos utilizar el propio python para verificar si un archivo es binario, porque se produce un error si se intenta abrir el archivo binario en modo texto

    def is_binary(file_name):
        try:
            with open(file_name, 'tr') as check_file:  # try open file in text mode
                check_file.read()
                return False
        except:  # if fail then file is non-text (binary)
            return True
    • Esto no pasa por un montón de `.avi’ (video) archivos.
  12. 1

    Llegué aquí buscando exactamente la misma cosa-una solución integral proporcionada por la biblioteca estándar para detectar de texto o binario. Después de revisar las opciones de la gente se ha sugerido, la nix archivo comando que parece ser la mejor opción (sólo estoy en vías de desarrollo para linux boxeo). Algunos otros publicados soluciones mediante el uso de archivo pero son innecesariamente complicados, en mi opinión, así que aquí está lo que se me ocurrió:

    def test_file_isbinary(filename):
        cmd = shlex.split("file -b -e soft '{}'".format(filename))
        if subprocess.check_output(cmd)[:4] in {'ASCI', 'UTF-'}:
            return False
        return True

    Debe ir sin decir, pero el código que llama a esta función debe asegurarse de que usted puede leer un archivo antes de la prueba, de lo contrario este será erróneamente detectar el archivo como binario.

  13. 1

    Supongo que la mejor solución es el uso de la guess_type función. Se tiene una lista con varios mimetypes y también puede incluir sus propios tipos.
    Aquí vienen los script que hice para solucionar mi problema:

    from mimetypes import guess_type
    from mimetypes import add_type
    
    def __init__(self):
            self.__addMimeTypes()
    
    def __addMimeTypes(self):
            add_type("text/plain",".properties")
    
    def __listDir(self,path):
            try:
                return listdir(path)
            except IOError:
                print ("The directory {0} could not be accessed".format(path))
    
    def getTextFiles(self, path):
            asciiFiles = []
            for files in self.__listDir(path):
                if guess_type(files)[0].split("/")[0] == "text":
                    asciiFiles.append(files)
            try:
                return asciiFiles
            except NameError:
                print ("No text files in directory: {0}".format(path))
            finally:
                del asciiFiles

    Es dentro de una Clase, como se puede ver, basado en la ustructure del código. Pero prácticamente se puede cambiar las cosas que desea implementar dentro de su aplicación.
    Es muy sencillo de usar.
    El método getTextFiles devuelve un objeto de la lista con todos los archivos de texto que se encuentra en el directorio de pasar en la variable path.

  14. 1

    Trate de usar la actualidad mantiene python-magia que no es el mismo módulo en @Kami Kisiel la respuesta. Este tiene soporte para todas las plataformas, incluyendo Windows, sin embargo, tendrás la libmagic archivos binarios. Esto se explica en el archivo LÉAME.

    A diferencia de la mimetypes módulo, no use la extensión del archivo y en su lugar se inspecciona el contenido del archivo.

    >>> import magic
    >>> magic.from_file("testdata/test.pdf", mime=True)
    'application/pdf'
    >>> magic.from_file("testdata/test.pdf")
    'PDF document, version 1.2'
    >>> magic.from_buffer(open("testdata/test.pdf").read(1024))
    'PDF document, version 1.2'
  15. 0

    están en unix? si es así, tratar:

    isBinary = os.system("file -b" + name + " | grep text > /dev/null")

    El shell de devolver los valores están invertidos (0 está bien, así que si se encuentra «texto» y, a continuación, se devolverá un 0, y en Python que es una Falsa expresión).

    • Esta obra basada en la extensión del archivo?
    • Para referencia, el archivo de comandos adivina un tipo basado en el contenido del archivo. No estoy seguro de si se presta atención a la extensión de archivo.
    • Estoy casi seguro de que se ve tanto en el contenido y la extensión.
    • De esta forma se rompe si la ruta de acceso contiene «texto», tho. Asegúrese de rsplit en el último ‘:’ (siempre que no haya dos puntos en el tipo de archivo descripción).
    • Uso file con el -b interruptor; que va a imprimir sólo el tipo de archivo sin la ruta de acceso.
    • un poco más agradable versión: is_binary_file = lambda filename: "text" in subprocess.check_output(["file", "-b", filename])

  16. 0

    Más sencillo es comprobar si el archivo constan carácter NULO (\x00) mediante el uso de in operador, por ejemplo:

    b'\x00' in open("foo.bar", 'rb').read()

    Ver más abajo el ejemplo completo:

    #!/usr/bin/env python3
    import argparse
    if __name__ == '__main__':
        parser = argparse.ArgumentParser()
        parser.add_argument('file', nargs=1)
        args = parser.parse_args()
        with open(args.file[0], 'rb') as f:
            if b'\x00' in f.read():
                print('The file is binary!')
            else:
                print('The file is not binary!')

    El ejemplo de uso:

    $ ./is_binary.py /etc/hosts
    The file is not binary!
    $ ./is_binary.py `which which`
    The file is binary!
  17. 0

    en *NIX:

    Si usted tiene acceso a la file de la shell de comandos, shlex puede ayudar a hacer que el módulo subprocess más utilizable:

    from os.path import realpath
    from subprocess import check_output
    from shlex import split
    
    filepath = realpath('rel/or/abs/path/to/file')
    assert 'ascii' in check_output(split('file {}'.format(filepth).lower()))

    O, también podría palo que en un bucle para obtener una salida para todos los archivos en el directorio actual mediante:

    import os
    for afile in [x for x in os.listdir('.') if os.path.isfile(x)]:
        assert 'ascii' in check_output(split('file {}'.format(afile).lower()))

    o para todos los subdirectorios:

    for curdir, filelist in zip(os.walk('.')[0], os.walk('.')[2]):
         for afile in filelist:
             assert 'ascii' in check_output(split('file {}'.format(afile).lower()))
    • OP claramente establecido Python
  18. 0

    La mayoría de los programas de considerar que el archivo es binario (es decir, cualquier archivo que no es «línea» orientadas a) si contiene un Carácter NULL.

    Aquí es de perl versión de pp_fttext() (pp_sys.c) implementado en Python:

    import sys
    PY3 = sys.version_info[0] == 3
    
    # A function that takes an integer in the 8-bit range and returns
    # a single-character byte object in py3 /a single-character string
    # in py2.
    #
    int2byte = (lambda x: bytes((x,))) if PY3 else chr
    
    _text_characters = (
            b''.join(int2byte(i) for i in range(32, 127)) +
            b'\n\r\t\f\b')
    
    def istextfile(fileobj, blocksize=512):
        """ Uses heuristics to guess whether the given file is text or binary,
            by reading a single block of bytes from the file.
            If more than 30% of the chars in the block are non-text, or there
            are NUL ('\x00') bytes in the block, assume this is a binary file.
        """
        block = fileobj.read(blocksize)
        if b'\x00' in block:
            # Files with null bytes are binary
            return False
        elif not block:
            # An empty file is considered a valid text file
            return True
    
        # Use translate's 'deletechars' argument to efficiently remove all
        # occurrences of _text_characters from the block
        nontext = block.translate(None, _text_characters)
        return float(len(nontext)) / len(block) <= 0.30

    Tenga en cuenta también que este código fue escrito para ejecutarse en Python 2 y Python 3 sin cambios.

    Fuente: Perl «adivinar si el archivo es de texto o binario», implementado en Python

Dejar respuesta

Please enter your comment!
Please enter your name here