如何在python中检测文件是否为二进制文件(非文本文件)?

我怎么能告诉如果一个文件是二进制(非文本)在Python中? 我正在通过python中的一大组文件进行search,并不断获取二进制文件中的匹配。 这使得输出看起来难以置信。

我知道我可以使用grep -I,但是我的数据比grep允许的要多。

在过去,我只是search大于0x7f的字符,但是utf8等等在现代系统中是不可能的。 理想情况下,解决scheme会很快,但任何解决scheme都可以。

你也可以使用mimetypes模块:

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

编译二进制MIMEtypes列表相当容易。 例如Apache分发一个mime.types文件,你可以parsing成一组列表,二进制和文本,然后检查MIME是否在你的文本或二进制列表中。

另一种基于文件(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)) 

例:

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

尝试这个:

 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 

如果有帮助,许多二进制types以一个幻数开始。 这是一个文件签名列表 。

这是一个使用Unix 文件命令的build议:

 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) 

用法示例:

 >>> istext('/ etc / motd') 
真正
 >>> istext('/ vmlinuz') 
假
 >>> 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')#在UTF-8上工作
真正

它有不能移植到Windows的缺点(除非你有类似的file命令),并且不得不为每个文件产生一个外部进程,这可能是不可口的。

使用二进制库( GitHub )。

这是非常简单的,并基于在这个stackoverflow问题中find的代码。

实际上你可以用2行代码编写这个代码,但是这个包不需要用跨平台的各种奇怪的文件types编写和彻底地testing这两行代码。

通常你必须猜测。

你可以看看扩展是一个线索,如果文件有他们。

您也可以识别已知的二进制格式,并忽略它们。

否则,看看你有什么比例的不可打印的ASCII字节,并从中猜测。

你也可以尝试从UTF-8解码,看看是否产生合理的输出。

如果您不在Windows上,则可以使用Python Magic来确定文件types。 然后你可以检查它是否是文本/ MIMEtypes。

较短的解决scheme,UTF-16警告:

 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 '\0' in block: return True return False 

我来到这里寻找完全相同的东西 – 标准库提供的全面解决scheme来检测二进制或文本。 审查人们build议的选项后,nix 文件命令看起来是最好的select(我只为linux boxen开发)。 其他一些人使用文件发布解决scheme,但在我看来,这些解决scheme是不必要的复杂,所以这就是我想到的:

 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 

它应该不用说,但是你调用这个函数的代码应该确保你可以在testing之前读取一个文件,否则这个文件会被错误地检测为二进制文件。

如果您使用的是python3(和utf-8),那么它非常简单,只需在文本模式下打开文件,如果得到UnicodeDecodeError停止处理。 在文本模式下处理文件时,Python3将使用unicode(而在二进制模式下处理bytearray) – 如果您的编码无法解码任意文件,则很有可能得到UnicodeDecodError 。 例:

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

我想最好的解决scheme是使用guess_type函数。 它拥有几个mimetypes列表,你也可以包括你自己的types。 这里来了我解决我的问题的脚本:

 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 

它是一个类的内部,你可以看到基于代码的结构。 但是你几乎可以改变你想要在应用程序中实现的东西。 它使用起来相当简单。 方法getTextFiles返回一个列表对象,其中所有的文本文件驻留在pathvariables中传递的目录中。

这是一个函数,首先检查文件是否以BOM开始,如果不是在初始8192字节内查找零字节:

 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 

从技术上讲,对UTF-8 BOM的检查是不必要的,因为它不应该包含所有实际用途的零字节。 但是,由于这是一种非常常见的编码,因此在开始时检查BOM是更快的,而不是扫描所有8192个字节的0。

你在unix吗? 如果是这样,那么试试:

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

shell的返回值是相反的(0是好的,所以如果它find“text”,那么它将返回一个0,在Python中是一个Falseexpression式)。

更简单的方法是通过in运算符中检查文件是否包含NULL字符( \x00 ),例如:

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

看下面的完整例子:

 #!/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!') 

示例用法:

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

大多数程序认为该文件是二进制文件(即任何不是“面向行”的文件),如果它们包含NULL字符的话 。

这里是在Python中实现的pp_fttext()pp_sys.c )的perl版本:

 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 

还要注意,这段代码被编写为可以在没有改变的情况下在Python 2和Python 3上运行。

来源: Perl的“猜如果文件是文本或二进制”在Python中实现

在* NIX:

如果您有权访问file shell命令,则shlex可以帮助使subprocess模块更加可用:

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

或者,您也可以将其粘贴在for循环中,以使用以下命令获取当前目录中所有文件的输出:

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

或所有的子目录:

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