Unicode (UTF-8) чтение и запись файлов в Python
У меня возникает некоторая нехватка мозгов в понимании чтения и записи текста в файл (Python 2.4).
# The string, which has an a-acute in it.
ss = u'Capit\xe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)
( "u'Capit\xe1n '", "' Capit\xc3\xa1n '" )
print ss, ss8
print >> open('f1','w'), ss8
>>> file('f1').read()
'Capit\xc3\xa1n\n'
Поэтому я ввожу Capit\xc3\xa1n
в свой любимый редактор, в файл f2.
Тогда:
>>> open('f1').read()
'Capit\xc3\xa1n\n'
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
>>> open('f1').read().decode('utf8')
u'Capit\xe1n\n'
>>> open('f2').read().decode('utf8')
u'Capit\\xc3\\xa1n\n'
Что я здесь не понимаю? Ясно, что есть какой-то жизненно важный бит магии (или здравого смысла), который я пропускаю. Что делает один тип текстовых файлов для правильного преобразования?
То, что я действительно не могу понять, является точкой зрения UTF-8, если вы не можете заставить Python распознать ее, когда она приходит извне. Может быть, я должен просто JSON сбросить строку и использовать это вместо этого, поскольку у этого есть видимое представление! Более того, существует ли представление ASCII этого объекта Unicode, которое Python будет распознавать и декодировать при входе из файла? Если да, то как мне его получить?
>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'
Ответы
Ответ 1
В обозначениях
u'Capit\xe1n\n'
"\ xe1" представляет собой только один байт. "\ x" сообщает вам, что "e1" находится в шестнадцатеричном формате.
Когда вы пишете
Capit\xc3\xa1n
в ваш файл есть "\ xc3". Это 4 байта, и в вашем коде вы их прочитали. Вы можете видеть это при их отображении:
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
Вы можете видеть, что обратная косая черта сбрасывается обратным слэшем. Таким образом, у вас есть четыре байта в вашей строке: "\", "x", "c" и "3".
Edit:
Как указывалось в ответах других, вы должны просто ввести символы в редакторе, и ваш редактор должен обработать преобразование в UTF-8 и сохранить его.
Если у вас на самом деле есть строка в этом формате, вы можете использовать кодек string_escape
для его декодирования в обычную строку:
In [15]: print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
Capitán
Результат - это строка, которая закодирована в UTF-8, где акцентированный символ представлен двумя байтами, которые были записаны \\xc3\\xa1
в исходной строке. Если вы хотите иметь строку юникода, вам нужно снова декодировать UTF-8.
К вашему правлению: у вас нет UTF-8 в вашем файле. Чтобы увидеть, как это будет выглядеть:
s = u'Capit\xe1n\n'
sutf8 = s.encode('UTF-8')
open('utf-8.out', 'w').write(sutf8)
Сравните содержимое файла utf-8.out
с содержимым файла, сохраненного в вашем редакторе.
Ответ 2
Вместо того, чтобы испортить методы кодирования и декодирования, мне легче указать кодировку при открытии файла. Модуль io
(добавлен в Python 2.6) предоставляет функцию io.open
, которая имеет параметр кодирования.
Используйте метод open из модуля io
.
>>>import io
>>>f = io.open("test", mode="r", encoding="utf-8")
Затем после вызова функции f read() возвращается объект кодированного Unicode.
>>>f.read()
u'Capit\xe1l\n\n'
Обратите внимание, что в Python 3 функция io.read
является псевдонимом для встроенной функции read
. Встроенная функция чтения поддерживает только аргумент кодирования в Python 3, а не Python 2.
Изменить: ранее этот ответ рекомендовал модуль codecs. Модуль codecs может вызвать проблемы при смешивании read()
и readline()
, поэтому в этом ответе теперь рекомендуется io.
Используйте метод open из модуля кодеков.
>>>import codecs
>>>f = codecs.open("test", "r", "utf-8")
Затем после вызова функции f read() возвращается объект кодированного Unicode.
>>>f.read()
u'Capit\xe1l\n\n'
Если вы знаете кодировку файла, использование пакета кодеков будет намного менее запутанным.
См. http://docs.python.org/library/codecs.html#codecs.open
Ответ 3
Итак, я нашел решение для того, что я ищу, а именно:
print open('f2').read().decode('string-escape').decode("utf-8")
Здесь есть некоторые необычные кодеки. Это конкретное чтение позволяет принимать UTF-8-представления из Python, копировать их в ASCII файл и считывать их в Unicode. При декодировании "строка-побег" косые черты не будут удвоены.
Это позволяет использовать ту поездку, которую я представлял себе.
Ответ 4
# -*- encoding: utf-8 -*-
# converting a unknown formatting file in utf-8
import codecs
import commands
file_location = "jumper.sub"
file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)
file_stream = codecs.open(file_location, 'r', file_encoding)
file_output = codecs.open(file_location+"b", 'w', 'utf-8')
for l in file_stream:
file_output.write(l)
file_stream.close()
file_output.close()
Ответ 5
Собственно, это работало для меня для чтения файла с кодировкой UTF-8 в Python 3.2:
import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
print(line)
Ответ 6
Теперь все, что вам нужно в Python3, это open(Filename, 'r', encoding='utf-8')
[Редактировать в 2016-02-10 за запрошенное разъяснение]
Python3 добавил параметр кодировки в свою открытую функцию. Ниже приведена следующая информация об открытой функции: https://docs.python.org/3/library/functions.html#open
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
Кодирование - это имя кодировки, используемой для декодирования или кодирования файл. Это следует использовать только в текстовом режиме. Кодировка по умолчанию зависит от платформы (независимо от locale.getpreferredencoding()возвращает), но можно использовать любую кодировку текста, поддерживаемую Python. См. codecs модуль для списка поддерживаемых кодировок.
Таким образом, добавив encoding='utf-8'
в качестве параметра для открытой функции, чтение и запись файлов выполняются как utf8 (который также теперь является кодировкой по умолчанию для всего, что сделано на Python.)
Ответ 7
Чтобы прочитать строку Unicode, а затем отправить в HTML, я сделал следующее:
fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace')
Полезно для http-серверов с поддержкой python.
Ответ 8
Хорошо, ваш любимый текстовый редактор не понимает, что \xc3\xa1
должен быть символьным литералом, но он интерпретирует их как текст. Вот почему вы получаете двойную обратную косую черту в последней строке - теперь это реальная обратная косая черта + xc3
и т.д. В вашем файле.
Если вы хотите читать и писать закодированные файлы в Python, лучше всего используйте модуль codecs.
Вставка текста между терминалом и приложениями сложна, потому что вы не знаете, какая программа будет интерпретировать ваш текст, используя какую кодировку. Вы можете попробовать следующее:
>>> s = file("f1").read()
>>> print unicode(s, "Latin-1")
Capitán
Затем вставьте эту строку в свой редактор и убедитесь, что она хранит ее, используя Latin-1. В предположении, что буфер обмена не искажает строку, поездка туда и обратно должна работать.
Ответ 9
Вы столкнулись с общей проблемой с кодировками: как я могу определить, в какой кодировке находится файл?
Ответ: вы не можете, если для этого используется формат файла. XML, например, начинается с:
<?xml encoding="utf-8"?>
Этот заголовок был тщательно выбран так, чтобы его можно было прочитать независимо от кодировки. В вашем случае нет такого намека, поэтому ни ваш редактор, ни Python не имеют представления о том, что происходит. Поэтому вы должны использовать модуль codecs
и использовать codecs.open(path,mode,encoding)
, который предоставляет отсутствующий бит в Python.
Что касается вашего редактора, вы должны проверить, предлагает ли он какой-либо способ установить кодировку файла.
Точка UTF-8 должна иметь возможность кодировать 21-битные символы (Unicode) в виде 8-битного потока данных (потому что это единственное, что могут обрабатывать все компьютеры в мире). Но поскольку большинство ОС предшествуют эпохе Unicode, у них нет подходящих инструментов для прикрепления информации о кодировке к файлам на жестком диске.
Следующая проблема - это представление в Python. Это объясняется в комментарии heikogerlach. Вы должны понимать, что ваша консоль может отображать только ASCII. Чтобы отобразить Unicode или что-нибудь >= charcode 128, он должен использовать некоторые способы экранирования. В редакторе вы не должны вводить экранированную строку отображения, но то, что означает строка (в этом случае вы должны ввести умлаут и сохранить файл).
Тем не менее, вы можете использовать функцию python eval(), чтобы превратить escape-строку в строку:
>>> x = eval("'Capit\\xc3\\xa1n\\n'")
>>> x
'Capit\xc3\xa1n\n'
>>> x[5]
'\xc3'
>>> len(x[5])
1
Как вы можете видеть, строка "\ xc3" была преобразована в один символ. Теперь это 8-битная строка, кодированная UTF-8. Чтобы получить Unicode:
>>> x.decode('utf-8')
u'Capit\xe1n\n'
Грегг Линд спросил: я думаю, что здесь есть некоторые фрагменты: файл f2 содержит: hex:
0000000: 4361 7069 745c 7863 335c 7861 316e Capit\xc3\xa1n
codecs.open('f2','rb', 'utf-8')
, например, читает их все в отдельных символах (ожидается) Есть ли способ записать в файл в ASCII, который будет работать?
Ответ: Это зависит от того, что вы имеете в виду. ASCII не может представлять символы > 127. Поэтому вам нужно как-то сказать "следующие несколько символов означают что-то особенное", что и делает последовательность "\ x". В нем говорится: Следующие два символа - это код одного символа. "\ u" делает то же самое с использованием четырех символов для кодирования Unicode до 0xFFFF (65535).
Таким образом, вы не можете напрямую писать Unicode в ASCII (поскольку ASCII просто не содержит одинаковых символов). Вы можете записать его как строки escape (как в f2); в этом случае файл может быть представлен как ASCII. Или вы можете записать его как UTF-8, и в этом случае вам нужен 8-разрядный безопасный поток.
Ваше решение с использованием decode('string-escape')
работает, но вы должны знать, сколько памяти вы используете: в три раза больше использования codecs.open()
.
Помните, что файл представляет собой просто последовательность байтов с 8 бит. Ни биты, ни байты не имеют значения. Это вы, который говорит "65 означает" A ". Поскольку \xc3\xa1
должен стать" à", но компьютер не имеет возможности знать, вы должны указать его, указав кодировку, которая была использована при записи файла.
Ответ 10
Последовательность\x.. является чем-то специфичным для Python. Это не универсальная escape-последовательность байта.
Как вы действительно входите в кодировку UTF-8, отличную от ASCII, зависит от вашей ОС и/или от вашего редактора. Вот как вы это делаете в Windows. Для того, чтобы OS X входила с острым акцентом, вы можете просто нажать option + E, затем A, и почти все текстовые редакторы в OS X поддерживают UTF-8.
Ответ 11
кроме codecs.open()
, можно использовать io.open()
для работы с Python2 или Python3 для чтения/записи файла Unicode
Пример
import io
text = u'á'
encoding = 'utf8'
with io.open('data.txt', 'w', encoding=encoding, newline='\n') as fout:
fout.write(text)
with io.open('data.txt', 'r', encoding=encoding, newline='\n') as fin:
text2 = fin.read()
assert text == text2
Ответ 12
Вы также можете улучшить исходную функцию open()
для работы с файлами Unicode, заменив ее на месте, используя функцию partial
. Красота этого решения - вам не нужно менять старый код. Это прозрачно.
import codecs
import functools
open = functools.partial(codecs.open, encoding='utf-8')
Ответ 13
Я пытался разобрать iCal с помощью Python 2.7.9:
из icalendar import Календарь
Но я получал:
Traceback (most recent call last):
File "ical.py", line 92, in parse
print "{}".format(e[attr])
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 7: ordinal not in range(128)
и это было исправлено с помощью:
print "{}".format(e[attr].encode("utf-8"))
(Теперь он может печатать как "böss.)