Unicode (UTF-8) чтение и запись в файлы на Python

у меня есть некоторые мозговые сбои в понимании чтения и записи текста в файл (Python 2.4).

# The string, which has an a-acute in it.
ss = u'Capitxe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)

("U'Capitxe1n'","'Capitxc3xa1n'")

print ss, ss8
print >> open('f1','w'), ss8

>>> file('f1').read()
'Capitxc3xa1nn'

поэтому я набираю Capitxc3xa1n в мой любимый редактор, в файле f2.

затем:

>>> open('f1').read()
'Capitxc3xa1nn'
>>> open('f2').read()
'Capitxc3xa1nn'
>>> open('f1').read().decode('utf8')
u'Capitxe1nn'
>>> open('f2').read().decode('utf8')
u'Capitxc3xa1nn'

чего я здесь не понимаю? Ясно, что есть какая-то жизненно важная часть магии (или здравого смысла), которую я упускаю. Что делает один тип в текстовые файлы, чтобы получить правильный конверсии?

то, что я действительно не могу grok здесь,-это то, в чем смысл представления UTF-8, Если вы не можете заставить Python распознать его, когда он приходит извне. Может быть, я должен просто JSON сбросить строку и использовать ее вместо этого, так как это имеет asciiable представление! Более того, существует ли ASCII-представление этого объекта Unicode, которое Python распознает и декодирует при входе из файла? Если да, то как мне его получить?

>>> print simplejson.dumps(ss)
'"Capitu00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capitxe1n'

13 ответов


в формате

u'Capit\xe1n\n'

"\xe1 " представляет только один байт. "\x "говорит вам, что" e1 " находится в шестнадцатеричном формате. Когда вы пишете

Capit\xc3\xa1n

в вашем файле у вас есть" \xc3 " в нем. Эти 4 байта и в вашем коде, вы прочитали их все. Вы можете увидеть это, когда вы показываете им:

>>> open('f2').read()
'Capit\xc3\xa1n\n'

вы можете видеть, что обратная косая черта экранируется обратной косой чертой. Таким образом, у вас есть четыре байта в строке:"\", "x", "c" и "3".

Edit:

As другие указали в своих ответах, что вы должны просто ввести символы в редакторе, и ваш редактор должен затем обработать преобразование в UTF-8 и сохранить его.

если у вас действительно есть строки в этом формате вы можете использовать string_escape кодек для декодирования его в нормальную строку:

In [15]: print 'Capit\xc3\xa1n\n'.decode('string_escape')
Capitán

результатом является строка, закодированная в UTF-8, где подчеркнутый символ представлен двумя байтами, которые были записаны \xc3\xa1 в исходной строке. Если вы хотите иметь строка unicode вы должны декодировать снова с UTF-8.

для вашего редактирования: у вас нет UTF-8 в вашем файле. Чтобы увидеть, как это будет выглядеть:

s = u'Capit\xe1n\n'
sutf8 = s.encode('UTF-8')
open('utf-8.out', 'w').write(sutf8)

сравните содержимое файла utf-8.out к содержимому файла, сохраненного с помощью редактора.


вместо того, чтобы возиться с методами кодирования и декодирования, мне проще указать кодировку при открытии файла. The io модуль (добавлено в Python 2.6) предоставляет io.open функция, которая имеет параметр кодирования.

используйте метод open из io модуль.

>>>import io
>>>f = io.open("test", mode="r", encoding="utf-8")

затем после вызова функции read() f возвращается закодированный объект Unicode.

>>>f.read()
u'Capit\xe1l\n\n'

обратите внимание, что в Python 3, то . Встроенная функция open поддерживает только аргумент кодировки в Python 3, а не Python 2.

Edit: ранее этот ответ рекомендовал кодеки модуль. The модуль кодеков может вызвать проблемы при смешивании read() и readline(), поэтому этот ответ теперь рекомендует io модуль.

используйте метод open из кодеков модуль.

>>>import codecs
>>>f = codecs.open("test", "r", "utf-8")

затем после вызова функции read() f возвращается закодированный объект Unicode.

>>>f.read()
u'Capit\xe1l\n\n'

если вы знаете кодировку файла, использование пакета кодеков будет гораздо менее запутанным.

см.http://docs.python.org/library/codecs.html#codecs.open


Теперь все, что вам нужно в Python3 это open(Filename, 'r', encoding='utf-8')

[правка на 2016-02-10 для запрошенных разъяснений]

Python3 добавил кодирование параметр к своей открытой функции. Здесь собрана следующая информация о функции open: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)

Encoding-это имя кодировки, используемой для декодирования или кодирования файл. Это должно используется только в текстовом режиме. Кодировку по умолчанию зависит от платформы (что угодно locale.getpreferredencoding () возвращается), но ни кодировка текста поддерживается Python может использоваться. Вижу кодеки модуль для списка поддерживаемых кодировок.

добавить encoding='utf-8' в качестве параметра функции open чтение и запись файлов выполняются как utf8 (что также является кодировкой по умолчанию для всего, что сделано в Питон.)


Итак, я нашел решение для того, что я ищу, а именно:

print open('f2').read().decode('string-escape').decode("utf-8")

есть несколько необычных кодеков, которые полезны здесь. Это конкретное чтение позволяет взять представления UTF-8 из Python, скопировать их в файл ASCII и прочитать их в Unicode. Под декодированием "string-escape" косые черты не будут удвоены.

Это позволяет для рода туда и обратно, что я воображал.


на самом деле, это работает для меня для чтения файла с кодировкой UTF-8 в Python 3.2:

import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
    print(line)

# -*- 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()

чтобы прочитать в строке Unicode, а затем отправить в HTML, я сделал следующее:

fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace')

полезно для HTTP-серверов на python.


за исключением 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

Ну, ваш любимый текстовый редактор не понимает, что \xc3\xa1 должны быть символьными литералами, но он интерпретирует их как текст. Вот почему вы получаете двойные косые черты в последней строке - теперь это настоящая обратная косая черта + xc3, etc. в вашем файле.

Если вы хотите читать и писать закодированные файлы на Python, лучше всего использовать кодеки модуль.

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

>>> s = file("f1").read()
>>> print unicode(s, "Latin-1")
Capitán

затем вставьте эту строку в Редактор и убедитесь, что она сохраняет ее с помощью латинского-1. В предположении, что буфер обмена не искажает строку, поездка туда и обратно должна работать.


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

ответ: вы не можете если формат файла предусматривает это. XML, например, начинается с:

<?xml encoding="utf-8"?>

этот заголовок был тщательно выбран, чтобы его можно было прочитать независимо от кодировки. В вашем случае такого намека нет, поэтому ни ваш редактор, ни Python понятия не имеют, что происходит. Поэтому вы должны использовать codecs модуль и использовать codecs.open(path,mode,encoding) который предоставляет недостающий бит в Python.

Что касается вашего редактора, вы должны проверить, предлагает ли он какой-либо способ установить кодировку файла.

точка UTF-8 должна иметь возможность кодировать 21-битные символы (Unicode) как 8-битный поток данных (потому что это единственное, что могут обрабатывать все компьютеры в мире). Но поскольку большинство OSs предшествуют эпохе Unicode, у них нет подходящих инструментов для прикрепления информации о кодировке к файлам на жестком диске диск.

следующая проблема-это представление в Python. Это прекрасно объясняется в комментарий heikogerlach. Вы должны понимать, что консоль может отображать только ASCII. Чтобы отобразить Unicode или что-либо >= charcode 128, он должен использовать некоторые средства экранирования. В Редакторе необходимо ввести не экранированную строку отображения, а значение строки (в этом случае необходимо ввести umlaut и сохранить файл).

что сказал, Вы можете использовать функция Python eval (), чтобы превратить экранированную строку в строку:

>>> x = eval("'Capit\xc3\xa1n\n'")
>>> x
'Capit\xc3\xa1n\n'
>>> x[5]
'\xc3'
>>> len(x[5])
1

как вы можете видеть, строка "\xc3 " была превращена в один символ. Теперь это 8-битная строка, кодированная UTF-8. Чтобы получить Юникод:

>>> 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 просто не содержит те же персонажи). Вы можете записать его как экранирование строк (как в f2); в этом случае файл может быть представлен как ASCII. Или вы можете написать его как UTF-8, в таком случае, вам нужен 8-битный безопасный поток.

ваше решение с помощью decode('string-escape') работает, но вы должны знать, сколько памяти вы используете: в три раза больше использования codecs.open().

помните, что файл-это просто последовательность байтов по 8 бит. Ни биты, ни байты не имеют значения. Это ты говоришь " 65 значит "а"". С \xc3\xa1 должно стать "à", но компьютер не имеет средств знать, вы должны сказать ему, указав кодировку, которая использовалась при записи файла.


\x.. последовательность-это то, что специфично для Python. Это не универсальная байтовая escape-последовательность.

Как вы на самом деле вводите в UTF-8-кодированный не ASCII, зависит от вашей ОС и/или вашего редактора. вот как вы это делаете в Windows. Для OS X ввести a С острым акцентом Вы можете просто нажать опции + E, потом A, и почти все текстовые редакторы в OS X поддерживают UTF-8.


вы также можете улучшить оригинал open() функция для работы с файлами Unicode, заменив его на месте, используя


Я пытался разобрать iCal использование Python 2.7.9:

из календаря импорта icalendar

, но я получаю:

 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.)