2016-10-03 62 views
-2

我正在从python 2开始将python应用程序转换为python 3.我使用的其中一个功能是从二进制文件中获取可打印字符。我刚才用下面的python 2的功能和它的伟大工作:如何在二进制文件中打印可打印字符(等价于Linux下的字符串)?

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它产生的输出这样上面的代码是绝对OK对我来说:

+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 

所以我由与此

更换此

if c in string.printable: 

转换的 'INT' 到 'STR'

(I也被转换,其中相同的错误消息被抛出的所有地方)

现在蟒3给出以下输出:

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

我不能看到任何字符时我使用Python 3。赞赏获得代码工作或指向解决方案的任何帮助。我所需要的只是从二进制文件中提取字符串(非常小,几kb),并将其存储在一个变量中。

+0

您在python3中有字节。使用'set(string.printable.encode())' –

+0

我不知道谁投下了这个问题。但是我要求他们以“Martijn Pieters先生”的回答方式显示文件和解释。如果显示,我将删除此帖子/问题。 –

回答

2

在Python 3中,以二进制模式打开文件会给出bytes结果。在bytes对象上执行迭代会给出整数,而不是字符,其范围为0到255(含)。从bytes documentation

While bytes literals and representations are based on ASCII text, bytes objects actually behave like immutable sequences of integers, with each value in the sequence restricted such that 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()分别在开始和结束时产生空值,如果输入以非打印字符开始或结束。

+0

并使用'result = b“”'。 –

+0

@MarkTolonen:最好使用'bytearray';你不能将整数附加到一个“字节”对象。 –

+0

是真的,它是那些令人惊讶的事情之一。迭代'str'并获得长度为1的strs,但迭代'bytes'并获得整数。无论如何'bytearray'更有意义。 'result + = bytes([c])'会起作用,但效率不高。 –

-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()的输出变换成一个可迭代。

说明:

  1. 导入需要的模块
  2. 定义功能
  3. 加载模块
  4. 创建缓冲区
  5. 从缓冲区
  6. 检查获得的一个字节,如果它是EOF
  7. 如果是,请停止发电机
  8. 尝试使用编码解码(如'\xef'不解码使用UTF-8)
  9. 如果是不可能的,它不能是可印刷
  10. 如果可打印,收率它

注:cp1252是许多文本文件的编码

+0

为什么使用'_io'而不是'io'? 'open()'已经返回一个缓冲读取器,为什么要重新包装呢?为什么要通过一些任意的8位编解码器进行解码? 'string.printable'中的所有字符都是ASCII字符;更好地在解码之前检测这些并避免该开销。而且由于您一次只读取1个字节,您无法使用任何多字节编解码器*;在文本模式下打开文件会更合乎逻辑。另外,不要使用blanket except语句;而是捕获特定的异常。 OP代码产生整个字符串,产生单个字节,这是没有用的。 –