Programing

파이썬에서 파일이 바이너리 (비 텍스트)인지 어떻게 감지 할 수 있습니까?

lottogame 2020. 8. 25. 19:18
반응형

파이썬에서 파일이 바이너리 (비 텍스트)인지 어떻게 감지 할 수 있습니까?


파이썬에서 파일이 바이너리 (텍스트가 아닌)인지 어떻게 알 수 있습니까?

파이썬에서 큰 파일 집합을 검색하고 있으며 이진 파일에서 계속 일치 항목을 얻습니다. 이것은 출력이 엄청나게 지저분하게 보입니다.

을 사용할 수 있다는 것을 알고 grep -I있지만 grep이 허용하는 것보다 더 많은 데이터를 사용하고 있습니다.

과거에, 난 그냥 이상의 문자를 검색 할 것이다 0x7f, 그러나 utf8와 같은은 현대적인 시스템이 불가능합니다. 이상적으로 솔루션은 빠르지 만 모든 솔루션이 가능합니다.


mimetypes 모듈을 사용할 수도 있습니다 .

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

바이너리 MIME 유형 목록을 컴파일하는 것은 매우 쉽습니다. 예를 들어 Apache는 목록, 바이너리 및 텍스트 세트로 구문 분석 할 수있는 mime.types 파일과 함께 배포 한 다음 mime이 텍스트 또는 바이너리 목록에 있는지 확인합니다.


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

utf-8과 함께 python3을 사용하는 경우 간단합니다. 텍스트 모드에서 파일을 열고 UnicodeDecodeError. Python3은 텍스트 모드 (및 바이너리 모드에서는 바이트 배열)에서 파일을 처리 할 때 유니 코드를 사용합니다. 인코딩이 임의의 파일을 디코딩 할 수없는 경우 UnicodeDecodeError.

예:

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

이 시도:

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

도움이된다면 많은 이진 유형이 매직 넘버로 시작됩니다. 다음은 파일 서명 목록 입니다.


다음은 Unix 파일 명령 을 사용하는 제안입니다 .

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명령어 와 같은 것이없는 경우), 각 파일에 대해 외부 프로세스를 생성해야하므로 맛이 좋지 않을 수 있습니다.


사용 binaryornot의 라이브러리 ( GitHub의 ).

이것은 매우 간단하며이 stackoverflow 질문에서 찾은 코드를 기반으로합니다.

실제로 2 줄의 코드로 작성할 수 있지만,이 패키지를 사용하면 모든 종류의 이상한 파일 유형 인 크로스 플랫폼으로 2 줄의 코드를 작성하고 철저히 테스트 할 필요가 없습니다.


보통 추측해야합니다.

파일에 확장자가 있으면 확장자를 하나의 단서로 볼 수 있습니다.

알고있는 바이너리 형식을 인식하고 무시할 수도 있습니다.

그렇지 않으면 인쇄 할 수없는 ASCII 바이트의 비율을 확인하고 그로부터 추측하십시오.

UTF-8에서 디코딩을 시도하고 이것이 적절한 출력을 생성하는지 확인할 수도 있습니다.


Windows가 아닌 경우 Python Magic 을 사용하여 파일 형식을 확인할 수 있습니다 . 그런 다음 텍스트 / 마임 유형인지 확인할 수 있습니다.


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

Here's a function that first checks if the file starts with a BOM and if not looks for a zero byte within the initial 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'\0' in initial_bytes

Technically the check for the UTF-8 BOM is unnecessary because it should not contain zero bytes for all practical purpose. But as it is a very common encoding it's quicker to check for the BOM in the beginning instead of scanning all the 8192 bytes for 0.


We can use python itself to check if a file is binary, because it fails if we try to open binary file in text mode

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

I came here looking for exactly the same thing--a comprehensive solution provided by the standard library to detect binary or text. After reviewing the options people suggested, the nix file command looks to be the best choice (I'm only developing for linux boxen). Some others posted solutions using file but they are unnecessarily complicated in my opinion, so here's what I came up with:

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

It should go without saying, but your code that calls this function should make sure you can read a file before testing it, otherwise this will be mistakenly detect the file as binary.


I guess that the best solution is to use the guess_type function. It holds a list with several mimetypes and you can also include your own types. Here come the script that I did to solve my problem:

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

It is inside of a Class, as you can see based on the ustructure of the code. But you can pretty much change the things you want to implement it inside your application. It`s quite simple to use. The method getTextFiles returns a list object with all the text files that resides on the directory you pass in path variable.


Try using the currently maintained python-magic which is not the same module in @Kami Kisiel's answer. This does support all platforms including Windows however you will need the libmagic binary files. This is explained in the README.

Unlike the mimetypes module, it doesn't use the file's extension and instead inspects the contents of the file.

>>> 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'

are you in unix? if so, then try:

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

The shell return values are inverted (0 is ok, so if it finds "text" then it will return a 0, and in Python that is a False expression).


Simpler way is to check if the file consist NULL character (\x00) by using in operator, for instance:

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

See below the complete example:

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

Sample usage:

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

on *NIX:

If you have access to the file shell-command, shlex can help make the subprocess module more usable:

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

Or, you could also stick that in a for-loop to get output for all files in the current dir using:

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

or for all 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()))

Most of the programs consider the file to be binary (which is any file that is not "line-oriented") if it contains a NULL character.

Here is perl's version of pp_fttext() (pp_sys.c) implemented in 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

Note also that this code was written to run on both Python 2 and Python 3 without changes.

Source: Perl's "guess if file is text or binary" implemented in Python

참고URL : https://stackoverflow.com/questions/898669/how-can-i-detect-if-a-file-is-binary-non-text-in-python

반응형