2011-03-09 56 views
2

我對這個有點頭痛,所以請提前請原諒我的術語。Python kludge以ASCII格式讀取UCS-2(UTF-16?)

我在Windows XP上運行Python 2.7。

我發現一些Python代碼讀取日誌文件,做一些東西,然後顯示一些東西。

什麼,那不夠詳細?好吧,這裏是一個簡化版本:

#!/usr/bin/python 

import re 
import sys 

class NotSupportedTOCError(Exception): 
    pass 

def filter_toc_entries(lines): 
    while True: 
     line = lines.next() 
     if re.match(r""" \s* 
        .+\s+ \| (?#track) 
       \s+.+\s+ \| (?#start) 
       \s+.+\s+ \| (?#length) 
       \s+.+\s+ \| (?#start sec) 
       \s+.+\s*$ (?#end sec) 
       """, line, re.X): 
      lines.next() 
      break 

    while True: 
     line = lines.next() 
     m = re.match(r""" 
      ^\s* 
      (?P<num>\d+) 
      \s*\|\s* 
      (?P<start_time>[0-9:.]+) 
      \s*\|\s* 
      (?P<length_time>[0-9:.]+) 
      \s*\|\s* 
      (?P<start_sector>\d+) 
      \s*\|\s* 
      (?P<end_sector>\d+) 
      \s*$ 
      """, line, re.X) 
     if not m: 
      break 
     yield m.groupdict() 

def calculate_mb_toc_numbers(eac_entries): 
    eac = list(eac_entries) 
    num_tracks = len(eac) 

    tracknums = [int(e['num']) for e in eac] 
    if range(1,num_tracks+1) != tracknums: 
     raise NotSupportedTOCError("Non-standard track number sequence: %s", tracknums) 

    leadout_offset = int(eac[-1]['end_sector']) + 150 + 1 
    offsets = [(int(x['start_sector']) + 150) for x in eac] 
    return [1, num_tracks, leadout_offset] + offsets 

f = open(sys.argv[1]) 

mb_toc_urlpart = "%20".join(str(x) for x in calculate_mb_toc_numbers(filter_toc_entries(f))) 

print mb_toc_urlpart 

的代碼工作正常,只要日誌文件是「簡單」的文字(我很想說ASCII儘管這可能並不精確/準確的 - 對於如記事本++表示它是ANSI)。

但是,腳本不能在某些日誌文件上工作(在這些情況下,Notepad ++會說「UCS-2 Little Endian」)。

我收到以下錯誤:

Traceback (most recent call last): 
    File "simple.py", line 55, in <module> 
    mb_toc_urlpart = "%20".join(str(x) for x in calculate_mb_toc_numbers(filter_ 
toc_entries(f))) 
    File "simple.py", line 49, in calculate_mb_toc_numbers 
    leadout_offset = int(eac[-1]['end_sector']) + 150 + 1 
IndexError: list index out of range 

此日誌works

此日誌breaks

我相信這是一個的突破腳本編碼,因爲如果我只是這樣做在命令提示符處:

type ascii.log > scrubbed.log 

a然後在scrubbed.log上運行該腳本,該腳本可以正常工作(對於我的目的來說,這確實很好,因爲沒有重要信息的丟失,並且我不寫回文件,只是打印到控制檯)。

一種解決方法是在將日誌文件傳遞給Python之前「擦洗」日​​志文件(例如,使用上面的類型管道技巧到臨時文件,然後讓腳本運行在該文件上),但是我希望Python「忽略「編碼,如果可能的話。我也不知道如何檢測腳本正在讀取什麼類型的日誌文件,因此我可以採取適當的行動。

我在讀thisthis但我的目光仍然在腦海中旋轉,所以雖然這可能是我的長期戰略,但我想知道是否有可以使用的臨時黑客攻擊。

+0

沒有任何答案解決問題了嗎?如果是這樣,請選擇一個(點擊答案旁邊的勾號);否則請說出來,我們會盡力幫助。 – 2011-03-10 19:12:29

回答

3

works.log出現在ASCII要被編碼:

>>> data = open('works.log', 'rb').read() 
>>> all(d < '\x80' for d in data) 
True 

breaks.log出現在UTF-16LE要被編碼 - 它與2個字節'\xff\xfe'開始。在breaks.log的人物沒有一個是ASCII範圍之外:

>>> data = open('breaks.log', 'rb').read() 
>>> data[:2] 
'\xff\xfe' 
>>> udata = data.decode('utf16') 
>>> all(d < u'\x80' for d in udata) 
True 

如果這些是僅有的兩種可能性,你應該能夠與下面的技巧脫身。從您的主線代碼更改:

f = open(sys.argv[1]) 
mb_toc_urlpart = "%20".join(
    str(x) for x in calculate_mb_toc_numbers(filter_toc_entries(f))) 
print mb_toc_urlpart 

這樣:

f = open(sys.argv[1], 'rb') 
data = f.read() 
f.close() 
if data[:2] == '\xff\xfe': 
    data = data.decode('utf16').encode('ascii') 
# ilines is a generator which produces newline-terminated strings 
ilines = (line + '\n' for line in data.splitlines()) 
mb_toc_urlpart = "%20".join(
    str(x) for x in calculate_mb_toc_numbers(filter_toc_entries(ilines))  ) 
print mb_toc_urlpart 
+0

感謝您的友好提醒。我仍在摸索代碼並計劃提出一個澄清的問題,但如果我把它作爲一個單獨的問題分解,可能會更好。在你的答案和伊格納西奧之間,我手裏有一條魚*,並且正在學魚 - 欣賞它。 – 2011-03-10 20:26:48

6

codecs.open()將允許您使用特定的編碼打開文件,它會產生unicode s。你可以嘗試一些,從最可能的最不可能的(或者工具可以總是生成UTF-16LE,但是哈哈有機會)。另外,"Unicode In Python, Completely Demystified"

+0

+1杉的神祕聯繫。從來沒有真正注意到這是很容易開始使用unicode – admalledd 2011-03-09 10:01:49

+0

這工作 - 謝謝。當John的回覆進來時,我試圖弄清楚如何基於編碼進行分支 - 我只希望我能接受兩個答案。 – 2011-03-10 20:26:06

0

Python 2.x預計正常字符串是ASCII(或至少一個字節)。試試這個:

在你的Python源文件的頂部將這個:

from __future__ import unicode_literals 

和更改所有的strunicode

[編輯]

而作爲伊格納西奧巴斯克斯 - 艾布拉姆斯寫道,嘗試codecs.open()打開輸入文件。

+0

這似乎沒有工作,但感謝從__future__'指針' – 2011-03-10 20:27:27