it-swarm.com.de

Wie kann ich feststellen, ob eine Datei in Python binär ist (kein Text)?

Wie kann ich feststellen, ob eine Datei in Python binär ist (kein Text)? Ich durchsuche eine große Anzahl von Dateien in Python und bekomme immer wieder Übereinstimmungen in Binärdateien. Dies macht die Ausgabe unglaublich unordentlich.

Ich weiß, ich könnte grep -I verwenden, aber ich mache mehr mit den Daten, als was grep zulässt.

In der Vergangenheit hätte ich einfach nach Zeichen gesucht, die größer als 0x7f sind, aber utf8 und ähnliches machen dies auf modernen Systemen unmöglich. Im Idealfall wäre die Lösung schnell, aber jede Lösung reicht aus.

83
grieve

Sie können auch das Modul mimetypes verwenden:

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

Es ist ziemlich einfach, eine Liste von binären Mime-Typen zusammenzustellen. Zum Beispiel verteilt Apache eine mime.types -Datei, die Sie in eine Liste von Listen, Binär- und Textlisten analysieren und dann prüfen können, ob sich der Mime in Ihrer Text- oder Binärliste befindet.

36
Gavin M. Roy

Noch eine andere Methode basierend auf Datei (1) Verhalten :

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

Beispiel:

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

Versuche dies:

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
11
Jorge Orpinel

Wenn es hilft, beginnen viele binäre Typen mit magischen Zahlen. Hier ist eine Liste von Dateisignaturen.

8
Shane C. Mason

Wenn Sie python3 mit utf-8 verwenden, ist dies unkompliziert. Öffnen Sie die Datei einfach im Textmodus und stoppen Sie die Verarbeitung, wenn Sie eine UnicodeDecodeError erhalten. Python3 verwendet Unicode beim Umgang mit Dateien im Textmodus (und Bytearray im Binärmodus). Wenn Ihre Kodierung keine beliebigen Dateien decodieren kann, ist es sehr wahrscheinlich, dass Sie UnicodeDecodeError erhalten. 

Beispiel:

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

Verwenden Sie binaryornot library ( GitHub ).

Es ist sehr einfach und basiert auf dem Code, der in dieser Stackoverflow-Frage gefunden wird.

Sie können dies zwar in zwei Zeilen Code schreiben, aber dieses Paket erspart Ihnen das Schreiben und gründliche Testen dieser beiden Codezeilen mit allen möglichen seltsamen Dateitypen (plattformübergreifend).

5
guettli

Hier ist ein Vorschlag, der den Unix-Befehl file verwendet:

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)

Verwendungsbeispiel:

 >>> istext ('/ etc/motd') 
 Richtig 
 >>> istext ('/ vmlinuz') 
 False 
 >>> offen ('/ tmp/japanese' ) .read () 
 '\ xe3\x81\x93\xe3\x82\x8c\xe3\x81\xaf\xe3\x80\x81\xe3\x81\xbf\xe3\xe3\x9a\xe3\x81\x8c\xe3\x82\x81\xe5\xba\xa7\xe3\x81\xae\xe6\x99\x82\xe4\xb3\xa3\xe3\x81\xe5\xe5\x9\x95\x8b\xe3\x81\x91\xe3\x80\x82\n '
 >>> istext ('/tmp/japanese ') # funktioniert unter UTF-8 
 Richtig 

Es hat den Nachteil, dass es nicht für Windows portierbar ist (es sei denn, Sie haben dort etwas wie den Befehl file), und muss für jede Datei einen externen Prozess erzeugen, der möglicherweise nicht schmackhaft ist.

5

Normalerweise muss man raten.

Sie können die Erweiterungen als Hinweis betrachten, wenn die Dateien über sie verfügen.

Sie können auch bekannte Binärformate erkennen und diese ignorieren.

Sehen Sie sich andernfalls den Anteil der nicht druckbaren ASCII - Bytes an und lassen Sie sich davon raten.

Sie können auch die Dekodierung von UTF-8 versuchen und sehen, ob dies zu einer sinnvollen Ausgabe führt.

4
Douglas Leeder

Wenn Sie nicht unter Windows sind, können Sie den Dateityp mithilfe von Python Magic ermitteln. Dann können Sie überprüfen, ob es sich um einen Text/Mime-Typ handelt.

3
Kamil Kisiel

Eine kürzere Lösung mit einer UTF-16-Warnung:

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
3
Tom Kennedy

Wir können Python selbst verwenden, um zu prüfen, ob eine Datei binär ist, da dies fehlschlägt, wenn wir versuchen, die Binärdatei im Textmodus zu öffnen

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
2
Serhii

Hier ist eine Funktion, die zuerst prüft, ob die Datei mit einer Stückliste beginnt und, falls nicht, innerhalb der ursprünglichen 8192 Bytes nach einem Null-Byte sucht:

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

Technisch ist die Prüfung der UTF-8-Stückliste nicht erforderlich, da sie aus praktischen Gründen nicht null Byte enthalten sollte. Da es sich jedoch um eine sehr häufige Kodierung handelt, ist es am Anfang schneller, nach der Stückliste zu suchen, als alle 8192 Bytes nach 0 zu durchsuchen.

1
roskakori

Ich denke, die beste Lösung ist die Verwendung der Funktion guess_type. Es enthält eine Liste mit mehreren Mimetypen und Sie können auch Ihre eigenen Typen einfügen .. Hier kommt das Skript, das ich zur Lösung meines Problems erstellt habe:

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 befindet sich innerhalb einer Klasse, wie Sie anhand der Struktur des Codes erkennen können. Sie können jedoch die Dinge, die Sie in Ihrer Anwendung implementieren möchten, ziemlich ändern. Die Verwendung der Methode getTextFiles gibt ein Listenobjekt mit allen Textdateien zurück, die sich in dem übergebenen Verzeichnis befinden in Pfadvariable.

1
Leonardo

Ich kam hierher und suchte genau das gleiche - eine umfassende Lösung, die von der Standardbibliothek zur Erkennung von Binär- oder Textdateien bereitgestellt wird. Nachdem ich die vorgeschlagenen Optionen durchgesehen habe, scheint der Befehl nix file die beste Wahl zu sein (ich entwickle nur für Linux Boxen). Einige andere veröffentlichten Lösungen mit file , aber sie sind meiner Meinung nach unnötig kompliziert, daher habe ich Folgendes herausgefunden:

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

Es sollte selbstverständlich sein, aber Ihr Code, der diese Funktion aufruft, sollte sicherstellen, dass Sie eine Datei lesen können, bevor Sie sie testen. Andernfalls wird die Datei versehentlich als binär erkannt.

1
rsaw

Versuchen Sie es mit dem aktuell gepflegten python-magic , das nicht dasselbe Modul in der Antwort von @Kami Kisiel ist. Dies unterstützt alle Plattformen, einschließlich Windows. Sie benötigen jedoch die Binärdateien libmagic. Dies wird in der README erklärt.

Im Gegensatz zum Modul mimetypes wird hier nicht die Dateierweiterung verwendet, sondern der Inhalt der Datei überprüft.

>>> 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'
1
Eat at Joes

Die meisten Programme betrachten die Datei als binär (dies ist eine Datei, die nicht "zeilenorientiert" ist), wenn sie ein NULL-Zeichen enthält.

Hier ist Perls Version von pp_fttext() (pp_sys.c) in Python implementiert:

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

Beachten Sie auch, dass dieser Code so geschrieben wurde, dass er auf Python 2 und Python 3 ohne Änderungen ausgeführt werden kann.

Quelle: Perls "erraten, ob die Datei Text oder Binärdatei ist" in Python implementiert

0
kenorb

Einfacher ist es, zu prüfen, ob die Datei aus NULL-Zeichen (\x00) besteht, indem Sie den Operator in verwenden.

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

Siehe das vollständige Beispiel:

#!/usr/bin/env python3
import argparse
if __== '__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!')

Verwendungsbeispiel:

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

bist du in unix Wenn ja, dann versuche es:

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

Die Shell-Rückgabewerte sind invertiert (0 ist in Ordnung, wenn also "Text" gefunden wird, wird eine 0 zurückgegeben, und in Python ist dies ein falscher Ausdruck).

0
fortran

an * NIX:

Wenn Sie auf den file Shell-Befehl zugreifen können, kann shlex dazu beitragen, das Subprozess-Modul nutzbarer zu machen:

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()))

Oder Sie können das auch in einer for-Schleife halten, um die Ausgabe für alle Dateien im aktuellen Verzeichnis mit Hilfe von:

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()))

oder für alle Subdirs:

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()))
0
Rob Truxal