如何在二进制文件中仅打印可打印的字符(与Linux下的字符串相当)?

时间:2016-10-03 16:32:44

标签: python string python-3.x

我正在将我的python应用程序从python 2转换为python 3.我使用的一个函数是从二进制文件中获取可打印字符。我之前在python 2中使用了以下函数,它运行得很好:

import string

def strings(filename, min=4):
    with open(filename, "rb") as f:
        result = ""
        for c in f.read():
            if c in string.printable:
                result += c
                continue
            if len(result) >= min:
                yield result
            result = ""
        if len(result) >= min:  # catch result at EOF
            yield result

代码实际上来自Python equivalent of unix "strings" utility。当我使用python 2运行上面的代码时,它产生这样的输出,这对我来说绝对没问题:

 +s
^!1^
i*Q(
}"~ 
%lh!ghY
#dh!
!`,!
mL#H
o!<XXT0
'   < 
z !Uk
%
 wS
n`  !wl
*ty

(Q  6
!XPLO$
E#kF

然而,该函数在python 3下给出了奇怪的结果。它产生错误:

TypeError: 'in <string>' requires string as left operand, not int

所以我通过替换

将'int'转换为'str'
if c in string.printable:

用这个

if str(c) in string.printable:

(我还转换了抛出相同错误消息的所有地方)

现在python 3提供了以下输出:

56700
0000000000000000000000000000000000000000
1236
60000
400234
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
2340
0000
5010
5000
17889
2348
23400000000
5600

当我使用python 3时,我无法看到任何字符。任何有助于使代码工作或指向解决方案的帮助都表示赞赏。我需要的是从二进制文件中提取字符串(非常小,只有几个kb)并将其存储在变量中。

2 个答案:

答案 0 :(得分:2)

在Python 3中,以二进制模式打开文件会为您提供bytes个结果。对bytes对象进行迭代可以得到整数,而不是字符,范围为​​0到255(含)。来自bytes documentation

  

虽然字节文字和表示基于ASCII文本,但bytes个对象实际上表现为不可变的整数序列,序列中的每个值都被限制为0 <= x < 256

string.printable转换为集合并对其进行测试:

printable = {ord(c) for c in string.printable}

if c in printable:

接下来,您希望附加到bytesarray()对象以保持合理的性能,并从ASCII解码以产生str结果:

printable = {ord(c) for c in string.printable}

with open(filename, "rb") as f:
    result = bytearray()
    for c in f.read():
        if c in printable:
            result.append(c)
            continue
        if len(result) >= min:
            yield result.decode('ASCII')
            result.clear()
    if len(result) >= min:  # catch result at EOF
        yield result

不是逐个迭代字节,而是可以拆分可打印的任何内容:

import re

nonprintable = re.compile(b'[^%s]+' % re.escape(string.printable.encode('ascii')))

with open(filename, "rb") as f:
    for result in nonprintable.split(f.read()):
        if result:
            yield result.decode('ASCII')

我会探索在中阅读文件,而不是一次性阅读;不要试图将大文件放入内存中:

with open(filename, "rb") as f:
    buffer = b''
    for chunk in iter(lambda: f.read(2048), b''):
        splitresult = nonprintable.split(buffer + chunk)            
        buffer = splitresult.pop()
        for string in splitresult:
            if string:
                yield string.decode('ascii')
    if buffer:
        yield buffer.decode('ascii')

缓冲区将任何不完整的单词从一个块转移到下一个块;如果输入分别以不可打印的字符开始或结束,re.split()会在开头和结尾生成空值。

答案 1 :(得分:-1)

我相信这会奏效。

作为发电机:

import string, _io
def getPrintablesFromBinaryFile(path, encoding='cp1252'):
    global _io, string
    buffer = _io.BufferedReader(open(path, 'rb'))
    while True:
        byte = buffer.read(1)
        if byte == b'':
            return #EOF
        try:
            d = byte.decode(encoding)
        except:
            continue
        if d in string.printable:
            yield d

作为一个函数,只需将getPrintablesFromBinaryFile()的输出收集到一个iterable中。

说明:

  1. 导入所需的模块
  2. 定义功能
  3. 加载模块
  4. 创建缓冲区
  5. 从缓冲区中获取一个字节
  6. 检查是否为EOF
  7. 如果是,请停止发电机
  8. 尝试使用编码进行解码(例如'\xef'不使用UTF-8进行解码)
  9. 如果不可能,则不能打印
  10. 如果可打印,请将其打印出来
  11. 注意: cp1252是许多文本文件的编码