Как прочитать файл, который может быть сохранен как ANSI или Unicode в Python? - PullRequest
2 голосов
/ 11 декабря 2011

Мне нужно написать скрипт, который поддерживает чтение файла, который можно сохранить как Unicode или Ansi (используя блокнот MS).

У меня нет никаких указаний о формате кодировки в файле, как я могу поддерживать оба формата кодирования? (это типичный способ чтения файлов без знания расширенного формата).

Ответы [ 2 ]

11 голосов
/ 12 декабря 2011

MS Notepad предоставляет пользователю выбор из 4 кодировок, выраженных в неуклюжей запутанной терминологии:

«Unicode» - это UTF-16, написанный как младший порядок.«Unicode big endian» - это UTF-16, написанный как big-endian.В обоих случаях UTF-16 это означает, что соответствующая спецификация будет записана.Используйте utf-16 для декодирования такого файла.

"UTF-8" - это UTF-8;Блокнот явно пишет "UTF-8 BOM".Используйте utf-8-sig для декодирования такого файла.

«ANSI» - шокер.Это терминология MS для «любой устаревшей кодировки по умолчанию на этом компьютере».

Вот список кодировок Windows, которые я знаю, и языки / сценарии, для которых они используются:

cp874  Thai
cp932  Japanese 
cp936  Unified Chinese (P.R. China, Singapore)
cp949  Korean 
cp950  Traditional Chinese (Taiwan, Hong Kong, Macao(?))
cp1250 Central and Eastern Europe 
cp1251 Cyrillic ( Belarusian, Bulgarian, Macedonian, Russian, Serbian, Ukrainian)
cp1252 Western European languages
cp1253 Greek 
cp1254 Turkish 
cp1255 Hebrew 
cp1256 Arabic script
cp1257 Baltic languages 
cp1258 Vietnamese
cp???? languages/scripts of India  

Если файл был создан на компьютере, на котором он читается, кодировку «ANSI» можно получить с помощью locale.getpreferredencoding().В противном случае, если вы знаете, откуда она взялась, вы можете указать, какую кодировку использовать, если это не UTF-16.В противном случае, угадайте.

Будьте осторожны, используя codecs.open() для чтения файлов в Windows.В документах говорится: "" "Примечание Файлы всегда открываются в двоичном режиме, даже если двоичный режим не указан. Это сделано для того, чтобы избежать потери данных из-за кодирования с использованием 8-битных значений. Это означает, что нет автоматического преобразования '\ n'сделано для чтения и записи. "" "Это означает, что ваши строки будут заканчиваться на \r\n, и вам понадобится / вы захотите удалить их.

Соберите все вместе:

Пример текстового файла, сохраненного со всеми четырьмя вариантами кодирования, выглядит в Блокноте следующим образом:

The quick brown fox jumped over the lazy dogs.
àáâãäå

Вот некоторый демонстрационный код:

import locale

def guess_notepad_encoding(filepath, default_ansi_encoding=None):
    with open(filepath, 'rb') as f:
        data = f.read(3)
    if data[:2] in ('\xff\xfe', '\xfe\xff'):
        return 'utf-16'
    if data == u''.encode('utf-8-sig'):
        return 'utf-8-sig'
    # presumably "ANSI"
    return default_ansi_encoding or locale.getpreferredencoding()

if __name__ == "__main__":
    import sys, glob, codecs
    defenc = sys.argv[1]
    for fpath in glob.glob(sys.argv[2]):
        print
        print (fpath, defenc)
        with open(fpath, 'rb') as f:
            print "raw:", repr(f.read())
        enc = guess_notepad_encoding(fpath, defenc)
        print "guessed encoding:", enc
        with codecs.open(fpath, 'r', enc) as f:
            for lino, line in enumerate(f, 1):
                print lino, repr(line)
                print lino, repr(line.rstrip('\r\n'))

, и вот вывод при запуске вОкно «Командная строка» Windows с использованием команды \python27\python read_notepad.py "" t1-*.txt

('t1-ansi.txt', '')
raw: 'The quick brown fox jumped over the lazy dogs.\r\n\xe0\xe1\xe2\xe3\xe4\xe5
\r\n'
guessed encoding: cp1252
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'

('t1-u8.txt', '')
raw: '\xef\xbb\xbfThe quick brown fox jumped over the lazy dogs.\r\n\xc3\xa0\xc3
\xa1\xc3\xa2\xc3\xa3\xc3\xa4\xc3\xa5\r\n'
guessed encoding: utf-8-sig
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'

('t1-uc.txt', '')
raw: '\xff\xfeT\x00h\x00e\x00 \x00q\x00u\x00i\x00c\x00k\x00 \x00b\x00r\x00o\x00w
\x00n\x00 \x00f\x00o\x00x\x00 \x00j\x00u\x00m\x00p\x00e\x00d\x00 \x00o\x00v\x00e
\x00r\x00 \x00t\x00h\x00e\x00 \x00l\x00a\x00z\x00y\x00 \x00d\x00o\x00g\x00s\x00.
\x00\r\x00\n\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\r\x00\n\x00'
guessed encoding: utf-16
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'

('t1-ucb.txt', '')
raw: '\xfe\xff\x00T\x00h\x00e\x00 \x00q\x00u\x00i\x00c\x00k\x00 \x00b\x00r\x00o\
x00w\x00n\x00 \x00f\x00o\x00x\x00 \x00j\x00u\x00m\x00p\x00e\x00d\x00 \x00o\x00v\
x00e\x00r\x00 \x00t\x00h\x00e\x00 \x00l\x00a\x00z\x00y\x00 \x00d\x00o\x00g\x00s\
x00.\x00\r\x00\n\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\r\x00\n'
guessed encoding: utf-16
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'

Что нужно знать:

(1) «mbcs» - это псевдокодировка файловой системы, которая не имеетотношение к декодированию содержимого файлов.В системе, где кодировка по умолчанию cp1252, она выглядит как latin1 (aarrgghh !!);см. ниже

>>> all_bytes = "".join(map(chr, range(256)))
>>> u1 = all_bytes.decode('cp1252', 'replace')
>>> u2 = all_bytes.decode('mbcs', 'replace')
>>> u1 == u2
False
>>> [(i, u1[i], u2[i]) for i in xrange(256) if u1[i] != u2[i]]
[(129, u'\ufffd', u'\x81'), (141, u'\ufffd', u'\x8d'), (143, u'\ufffd', u'\x8f')
, (144, u'\ufffd', u'\x90'), (157, u'\ufffd', u'\x9d')]
>>>

(2) chardet очень хорошо распознает кодировки на основе нелатинских шрифтов (китайский / японский / корейский, кириллица, иврит, греческий), но не очень хорош в латиницекодировки (Западная / Центральная / Восточная Европа, турецкая, вьетнамская) и вообще не владеют арабским языком.

3 голосов
/ 11 декабря 2011

Блокнот сохраняет файлы Unicode с меткой порядка байтов. Это означает, что первые байты файла будут:

  • EF BB BF - UTF-8
  • FF FE - "Unicode" (на самом деле UTF-16 с прямым порядком байтов, выглядит как)
  • FE FF - "Юникод big-endian" (выглядит как UTF-16 big-endian)

Другие текстовые редакторы могут иметь или не иметь такое же поведение, но если вы точно знаете, что используется Блокнот, это даст вам неплохую эвристику для автоматического выбора кодировки. Однако все эти последовательности действительны и в кодировке ANSI, поэтому такая эвристика может ошибаться. Невозможно гарантировать, что используется правильная кодировка.

...