Как я могу определить, является ли файл двоичным (нетекстовым) в python?
Как я могу сказать, является ли файл двоичным (нетекстовым) в python? Я просматриваю большой набор файлов в python и продолжаю получать совпадения в двоичных файлах. Это делает вывод выглядеть невероятно грязно.
Я знаю, что могу использовать grep-I, но я делаю больше с данными, чем позволяет grep.
в прошлом я бы просто искал символы больше 0x7f, но utf8 и тому подобное делают это невозможным в современных системах. В идеале решение было бы быстро, но любое решение подойдет.
18 ответов:
вы также можете использовать mimetypes модуль:
import mimetypes ... mime = mimetypes.guess_type(file)Это довольно легко составить список двоичных типов mime. Например Apache распространяет с помощью mime.типы файлов, которые можно разобрать на набор списков, двоичных и текстовых, а затем проверить, находится ли 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 <[email protected]> @author: Jorge Orpinel <[email protected]>""" fin = open(filename, 'rb') try: CHUNKSIZE = 1024 while 1: chunk = fin.read(CHUNKSIZE) if '' 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
Если это поможет, многие двоичные типы начинаются с магических чисел. здесь сигнатур файлов.
вот предложение, которое использует Unix file команда:
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') 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у него есть недостатки, которые не переносятся в Windows (Если у вас нет чего-то вроде
fileкоманда там), и необходимость порождать внешний процесс для каждого файла, который может быть не приемлемым.
использовать binaryornot библиотека (GitHub).
Это очень просто и основано на коде, найденном в этом вопросе stackoverflow.
вы можете написать это в 2 строках кода, однако этот пакет избавляет вас от необходимости писать и тщательно тестировать эти 2 строки кода со всеми видами странных типов файлов, кросс-платформенных.
обычно вы должны угадать.
вы можете посмотреть на расширения как один ключ, если файлы имеют их.
вы также можете распознать знать двоичные форматы, и игнорировать их.
в противном случае посмотрите, какая доля непечатаемых ASCII-байтов у вас есть, и предположите это.
вы также можете попробовать декодирование из UTF-8 и посмотреть, дает ли это разумный выход.
Если вы не на Windows, вы можете использовать Python Magic для определения типа файла. Затем вы можете проверить, является ли это типом text/ mime.
более короткое решение, с предупреждением 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 b'' in block: return True return False
Если вы используете python3 с utf-8 это прямо вперед, просто откройте файл в текстовом режиме и остановить обработку, если вы получаете
UnicodeDecodeError. Python3 будет использовать unicode при обработке файлов в текстовом режиме (и bytearray в двоичном режиме) - если ваша кодировка не может декодировать произвольные файлы, вполне вероятно, что вы получитеUnicodeDecodeError.пример:
try: with open(filename, "r") as f: for l in f: process_line(l) except UnicodeDecodeError: pass # Fond non-text data
Я пришел сюда, чтобы найти точно то же самое-комплексное решение, предоставляемое стандартной библиотекой для обнаружения двоичных или текстовых файлов. После просмотра вариантов, предложенных людьми, nix file команда выглядит лучшим выбором (я разрабатываю только для Linux boxen). Некоторые другие опубликовали решения с использованием file но они излишне сложны на мой взгляд, так что вот что я придумал:
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это должно быть само собой разумеется, но ваш код, который вызывает эту функцию, должен убедиться, что вы можете прочитать файл перед его тестированием, иначе это будет ошибочно определять файл как двоичный.
Я думаю, что лучшим решением является использование функции guess_type. Он содержит список с несколькими mimetypes, и вы также можете включить свои собственные типы. Вот сценарий, который я сделал, чтобы решить мою проблему:
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 возвращает объект списка со всем текстом файлы, которые находятся в каталоге, который вы передаете в переменной path.
вот функция, которая сначала проверяет, начинается ли файл с спецификации, и если нет, ищет нулевой байт в начальных 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'' in initial_bytesтехнически проверка спецификации UTF-8 не нужна, поскольку она не должна содержать нулевых байтов для всех практических целей. Но поскольку это очень распространенная кодировка, то быстрее проверить спецификацию в начале, а не сканировать все 8192 байта для 0.
мы можем использовать сам python, чтобы проверить, является ли файл двоичным, потому что он не работает, если мы пытаемся открыть двоичный файл в текстовом режиме
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
вы в unix? если да, то попробуйте:
isBinary = os.system("file -b" + name + " | grep text > /dev/null")возвращаемые значения оболочки инвертируются (0-это нормально, поэтому если он найдет "текст", то он вернет 0, а в Python это ложное выражение).
более простой способ-проверить, состоит ли файл из нулевого символа (
\x00) С помощьюinоператора, например: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!
on * NIX:
если у вас есть доступ к
fileshell-command, shlex может помочь сделать модуль подпроцесса более удобным: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()))
большинство программ считают файл двоичным (это любой файл, который не является "линейно-ориентированным"), если он содержит символ.
вот версия perl
pp_fttext()(pp_sys.c) реализовано в 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Отметим также, что этот код был написан для запуска на Python 2 и Python 3 без изменений.
источник: Perl "Угадай, если файл является текстовым или двоичным" реализован в Python
Comments