2016-10-03 85 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代碼產生整個字符串,產生單個字節,這是沒有用的。 –