Python - читать текстовый файл со странным форматом utf-16
Я пытаюсь прочитать текстовый файл в Python, но он, кажется, использует очень странную кодировку. Я стараюсь как обычно:
file = open('data.txt','r')
lines = file.readlines()
for line in lines[0:1]:
print line,
print line.split()
Выход:
0.0200197 1.97691e-005
['0\x00.\x000\x002\x000\x000\x001\x009\x007\x00', '\x001\x00.\x009\x007\x006\x009\x001\x00e\x00-\x000\x000\x005\x00']
Печать линии работает нормально, но после того, как я попытаюсь разбить линию, чтобы преобразовать ее в число с плавающей точкой, она выглядит сумасшедшей. Конечно, когда я пытаюсь преобразовать эти строки в числа с плавающей точкой, это приводит к ошибке. Любая идея о том, как я могу преобразовать их обратно в числа?
Я поместил образец файла данных здесь, если вы хотите попробовать загрузить его: https://dl.dropboxusercontent.com/u/3816350/Posts/data.txt
Я хотел бы просто использовать numpy.loadtxt или numpy.genfromtxt, но они также не хотят иметь дело с этим сумасшедшим файлом.
4 ответа
Готов поспорить, что это файл UTF-16-LE, и вы читаете его как любую кодировку по умолчанию.
В UTF-16 каждый символ занимает два байта.* Если все ваши символы являются ASCII, это означает, что кодировка UTF-16 выглядит как кодировка ASCII с дополнительным символом \x00 после каждого символа.
Чтобы это исправить, просто декодируйте данные:
print line.decode('utf-16-le').split()
Или сделайте то же самое на уровне файлов с модулем io или codecs:
file = io.open('data.txt','r', encoding='utf-16-le')
* Это немного упрощение: каждый символ BMP занимает два байта; каждый не-BMP символ превращается в суррогатную пару, причем каждый из двух суррогатов занимает два байта. Но вы, вероятно, не заботились об этих деталях.
Похоже, UTF-16 для меня.
>>> test_utf16 = '0\x00.\x000\x002\x000\x000\x001\x009\x007\x00'
>>> test_utf16.decode('utf-16')
u'0.0200197'
Вы можете работать непосредственно со строками Unicode:
>>> float(test_utf16)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: null byte in argument for float()
>>> float(test_utf16.decode('utf-16'))
0.020019700000000001
Или закодируйте их чем-то другим, если вы предпочитаете:
>>> float(test_utf16.decode('utf-16').encode('ascii'))
0.020019700000000001
Обратите внимание, что вам нужно сделать это как можно раньше при обработке. Как отметил ваш комментарий, split
будет некорректно вести себя в кодированной форме utf-16. UTF-16 представление символа пространства ' '
является ' \x00'
, так split
удаляет пробел, но оставляет нулевой байт.
2.6 и позже io
библиотека может справиться с этим для вас, как и старые codecs
библиотека. io
лучше обрабатывает переводы строк, поэтому желательно, если доступно.
Это на самом деле просто предложение @abarnert, но я хотел опубликовать его как ответ, так как это самое простое решение, которое я в итоге использовал:
file = io.open(filename,'r',encoding='utf-16-le')
data = np.loadtxt(file,skiprows=8)
Это демонстрирует, как вы можете создать объект файла, используя io.open, используя любую сумасшедшую кодировку, которая есть в вашем файле, и затем передать этот объект файла в np.loadtxt (или np.genfromtxt) для быстрой и легкой загрузки.
Этот кусок кода сделает все необходимое
file_handle=open(file_name,'rb')
file_first_line=file_handle.readline()
file_handle.close()
print file_first_line
if '\x00' in file_first_line:
file_first_line=file_first_line.replace('\x00','')
print file_first_line
Когда вы пытаетесь использовать file_first_line.split() перед заменой, вывод будет содержать "\x00", я просто попытался заменить "\x00" пустым, и это сработало.