Как я могу определить, является ли файл двоичным (нетекстовым) в Python? - PullRequest
88 голосов
/ 22 мая 2009

Как узнать, является ли файл двоичным (нетекстовым) в python? Я ищу большой набор файлов в Python и продолжаю получать совпадения в двоичных файлах. Это делает вывод выглядеть невероятно грязным.

Я знаю, что могу использовать grep -I, но я делаю с данными больше, чем позволяет grep.

В прошлом я бы просто искал символы больше 0x7f, но utf8 и тому подобное сделали это невозможным в современных системах. В идеале решение будет быстрым, но подойдет любое решение.

Ответы [ 19 ]

2 голосов
/ 24 июля 2018

Мы можем использовать сам 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
2 голосов
/ 04 мая 2017

Вот функция, которая сначала проверяет, начинается ли файл с спецификации, а если нет, ищет нулевой байт в начальных 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 не требуется, поскольку она не должна содержать нулевых байтов для всех практических целей. Но поскольку это очень распространенная кодировка, вначале быстрее проверять спецификацию, а не сканировать все 8192 байта на 0.

1 голос
/ 02 августа 2010

Полагаю, что лучшим решением является использование функции guess_type. Он содержит список из нескольких типов MIME, и вы также можете включать свои собственные типы. Вот сценарий, который я сделал, чтобы решить мою проблему:

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 возвращает объект списка со всеми текстовыми файлами, которые находятся в каталоге, который вы передаете в переменной пути.

1 голос
/ 04 января 2012

Я пришел сюда в поисках точно такого же - комплексного решения, предоставляемого стандартной библиотекой для обнаружения двоичного или текстового содержимого. Изучив предложенные варианты, команда nix file выглядит лучшим выбором (я разрабатываю только для linux boxen). Некоторые другие опубликовали решения, использующие файл , но, на мой взгляд, они неоправданно сложны, поэтому вот что я придумал:

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

Само собой разумеется, но ваш код, который вызывает эту функцию, должен убедиться, что вы можете прочитать файл перед его тестированием, в противном случае это будет ошибочно определять файл как двоичный.

1 голос
/ 17 июня 2019

Попробуйте использовать текущую python-magic , которая не является тем же модулем в ответе @Kami Kisiel. Это поддерживает все платформы, включая Windows, однако вам понадобятся двоичные файлы libmagic. Это объясняется в README.

В отличие от модуля mimetypes , он не использует расширение файла и вместо этого проверяет содержимое файла.

>>> 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'
0 голосов
/ 22 мая 2009

ты в юниксе? если так, то попробуйте:

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

Возвращаемые значения оболочки инвертированы (0 - нормально, поэтому, если он найдет «текст», он вернет 0, а в Python это выражение False).

0 голосов
/ 30 мая 2017

в * NIX:

Если у вас есть доступ к команде оболочки file, 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()))
0 голосов
/ 01 июня 2015

Большинство программ считают, что файл является двоичным (то есть любой файл, который не является "ориентированным на строки"), если он содержит NULL символ .

Вот версия 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

0 голосов
/ 01 июня 2015

Более простой способ - проверить, содержит ли файл символ NULL (\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!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...