Python Kludge для чтения UCS-2 (UTF-16?) Как ASCII - PullRequest
2 голосов
/ 09 марта 2011

Я немного об этом думаю, поэтому, пожалуйста, заранее извините мою терминологию.

Я использую Python 2.7 на Windows XP.

Я нашелнекоторый код 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, хотя это может быть неточным / точным - например,Notepad ++ указывает, что это 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

Этот журнал работает

Этот журнал прерывается

Я считаю, что кодировка нарушает сценарийпотому что, если я просто делаю это в командной строке:

type ascii.log > scrubbed.log

и затем запускаю сценарий на scrubbed.log, сценарий работает нормально (это на самом деле хорошо для моих целей, так как нет потери важной информации иЯ не записываю обратно в файл, просто печатаю на консоль).

Одним из обходных путей может быть «очистка» файла журнала перед передачей его в Python (например, с помощью описанного выше трюка с типом канала для временногофайл, а затем запустить скрипт на этом), но я бы хотел, чтобы Python «игнорировал» кодировку, если это возможно.Я также не уверен, как определить тип файла журнала, который читает скрипт, чтобы я мог действовать соответствующим образом.

Я читаю это и это , номои глаза все еще вращаются в их голове, поэтому, хотя это может быть моей долгосрочной стратегией, мне интересно, есть ли временный взлом, который я мог бы использовать.

Ответы [ 3 ]

6 голосов
/ 09 марта 2011

codecs.open() позволит вам открыть файл, используя определенную кодировку, и будет выдавать unicode s.Вы можете попробовать несколько, переходя от наиболее вероятного к наименее вероятному (или инструмент может всегда производить UTF-16LE, но у него есть большой шанс).

Также, "Unicode In Python, полностью демистифицирован".

3 голосов
/ 09 марта 2011

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 голосов
/ 09 марта 2011

Python 2.x ожидает, что нормальные строки будут ASCII (или хотя бы один байт).Попробуйте это:

Поместите это в начало вашего исходного файла Python:

from __future__ import unicode_literals

И измените все str на unicode.

[править]

И, как писал Игнасио Васкес-Абрамс, попробуйте codecs.open(), чтобы открыть входной файл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...