it-swarm.dev

Unicode (UTF-8) lendo e gravando em arquivos em Python

Estou tendo alguma falha cerebral em entender ler e escrever texto em um arquivo (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'

Então eu digito Capit\xc3\xa1n no meu editor favorito, no arquivo f2.

Então:

>>> 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'

O que eu não estou entendendo aqui? Claramente, há um pouco de mágica vital (ou bom senso) que estou perdendo. O que se digita em arquivos de texto para obter conversões adequadas?

O que eu realmente estou falhando aqui é qual é o ponto da representação UTF-8, se você não consegue realmente fazer com que o Python a reconheça, quando vem de fora. Talvez eu deva apenas JSON despejar a string, e usar isso em vez disso, desde que tenha uma representação ascenciável! Mais precisamente, há uma representação ASCII desse objeto Unicode que o Python reconhecerá e decodificará quando chegar de um arquivo? Se sim, como faço para obtê-lo?

>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'
290
Gregg Lind

Na notação

u'Capit\xe1n\n'

o "\ xe1" representa apenas um byte. "\ x" informa que "e1" está em hexadecimal. Quando você escreve

Capit\xc3\xa1n

no seu arquivo você tem "\ xc3" nele. Esses são 4 bytes e no seu código você lê todos eles. Você pode ver isso quando você os exibe:

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

Você pode ver que a barra invertida é escapada por uma barra invertida. Então você tem quatro bytes em sua string: "\", "x", "c" e "3".

Editar:

Como outros apontaram em suas respostas, você deve apenas digitar os caracteres no editor e seu editor deve então manipular a conversão para UTF-8 e salvá-lo.

Se você realmente tem uma string neste formato, você pode usar o codec string_escape para decodificá-lo em uma string normal:

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

O resultado é uma string codificada em UTF-8 onde o caractere acentuado é representado pelos dois bytes que foram escritos \\xc3\\xa1 na string original. Se você quiser ter uma string unicode, você terá que decodificar novamente com UTF-8.

Para sua edição: você não tem UTF-8 no seu arquivo. Para realmente ver como ficaria:

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

Compare o conteúdo do arquivo utf-8.out com o conteúdo do arquivo que você salvou com seu editor.

99
unbeknown

Em vez de mexer nos métodos de codificação e decodificação, é mais fácil especificar a codificação ao abrir o arquivo. O io module (adicionado no Python 2.6) fornece uma função io.open, que possui um parâmetro de codificação.

Use o método open do módulo io.

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

Depois de chamar a função read () de f, um objeto Unicode codificado é retornado.

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

Note que no Python 3, a função io.open é um alias para a função open incorporada. A função open interna suporta apenas o argumento de codificação no Python 3, não no Python 2.

Edit: Anteriormente esta resposta recomendou o módulo codecs . O módulo de codecs pode causar problemas ao misturar read() e readline() , então esta resposta agora recomenda o módulo io

Use o método aberto no módulo de codecs.

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

Depois de chamar a função read () de f, um objeto Unicode codificado é retornado.

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

Se você souber a codificação de um arquivo, usar o pacote de codecs será muito menos confuso.

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

659
Tim Swast

Agora tudo que você precisa no Python3 é open(Filename, 'r', encoding='utf-8')

[Edit on 2016-02-10 para esclarecimentos solicitados]

O Python3 adicionou o parâmetro de codificação à sua função aberta. As seguintes informações sobre a função aberta são reunidas aqui: 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)

Codificação é o nome da codificação usada para decodificar ou codificar o arquivo. Isso só deve ser usado no modo de texto. A codificação padrão é dependente da plataforma (whatever locale.getpreferredencoding () retorna), mas qualquer codificação de texto suportado pelo Python pode ser usado. Veja o módulo codecs para a lista de codificações suportadas.

Então, adicionando encoding='utf-8' como um parâmetro para a função open, o arquivo lendo e escrevendo é feito como utf8 (que também é agora a codificação padrão de tudo que é feito em Python).

31
Dakusan

Então, encontrei uma solução para o que estou procurando, que é:

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

Existem alguns codecs incomuns que são úteis aqui. Essa leitura em particular permite que você obtenha representações UTF-8 de dentro do Python, copie-as em um arquivo ASCII e faça com que elas sejam lidas em Unicode. Sob o decodificador "string-escape", as barras não serão duplicadas.

Isso permite o tipo de viagem de ida e volta que eu estava imaginando.

17
Gregg Lind
# -*- 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()
14
Ricardo

Na verdade, isso funcionou para mim para ler um arquivo com codificação UTF-8 no Python 3.2:

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

Para ler em uma string Unicode e, em seguida, enviar para HTML, fiz o seguinte:

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

Útil para servidores http alimentados por python.

6
praj

exceto para codecs.open(), pode-se usar io.open() para trabalhar com Python2 ou Python3 para ler/escrever um arquivo unicode

exemplo

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
6
Ryan

Você tropeçou no problema geral com codificações: Como posso saber em qual codificação um arquivo está?

Resposta: Você não pode a menos que o formato de arquivo forneça isso. XML, por exemplo, começa com:

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

Este cabeçalho foi cuidadosamente escolhido para que possa ser lido independentemente da codificação. No seu caso, não existe essa dica, por isso nem o seu editor nem o Python têm ideia do que está acontecendo. Portanto, você deve usar o módulo codecs e usar codecs.open(path,mode,encoding), que fornece o bit ausente no Python.

Quanto ao seu editor, você deve verificar se ele oferece alguma maneira de definir a codificação de um arquivo.

O ponto do UTF-8 é poder codificar caracteres de 21 bits (Unicode) como um fluxo de dados de 8 bits (porque essa é a única coisa que todos os computadores do mundo podem manipular). Mas como a maioria dos sistemas operacionais é anterior à era Unicode, eles não possuem ferramentas adequadas para anexar as informações de codificação aos arquivos no disco rígido.

A próxima questão é a representação em Python. Isto é explicado perfeitamente no comentário por heikogerlach . Você deve entender que seu console só pode exibir ASCII. Para exibir Unicode ou qualquer coisa> = charcode 128, ele deve usar algum meio de escape. Em seu editor, você não deve digitar a sequência de exibição de escape, mas o que a sequência de caracteres significa (nesse caso, você deve inserir o trema e salvar o arquivo).

Dito isso, você pode usar a função do Python eval () para transformar uma string de escape em uma string:

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

Como você pode ver, a string "\ xc3" foi transformada em um único caractere. Esta é agora uma string de 8 bits, codificada em UTF-8. Para obter o Unicode:

>>> x.decode('utf-8')
u'Capit\xe1n\n'

Gregg Lind perguntei: Eu acho que existem algumas partes faltando aqui: o arquivo f2 contém: hex:

0000000: 4361 7069 745c 7863 335c 7861 316e  Capit\xc3\xa1n

codecs.open('f2','rb', 'utf-8'), por exemplo, lê todos eles em caracteres separados (esperados) Existe alguma maneira de gravar em um arquivo em ASCII que funcionaria?

Resposta: Isso depende do que você quer dizer. ASCII não pode representar caracteres> 127. Então, você precisa de alguma maneira de dizer "os próximos caracteres significam algo especial", que é o que a sequência "\ x" faz. Diz: Os próximos dois caracteres são o código de um único caractere. "\ u" faz o mesmo usando quatro caracteres para codificar Unicode até 0xFFFF (65535).

Portanto, você não pode gravar diretamente Unicode em ASCII (porque ASCII simplesmente não contém os mesmos caracteres). Você pode escrevê-lo como escape de string (como em f2); Nesse caso, o arquivo pode ser representado como ASCII. Ou você pode escrever como UTF-8, nesse caso, você precisa de um fluxo seguro de 8 bits.

Sua solução usando decode('string-escape') funciona, mas você deve estar ciente de quanta memória você usa: três vezes a quantidade de uso codecs.open().

Lembre-se que um arquivo é apenas uma seqüência de bytes com 8 bits. Nem os bits nem os bytes têm um significado. É você quem diz "65 significa 'A'". Como \xc3\xa1 deve se tornar "à", mas o computador não tem como saber, você deve informar isso especificando a codificação que foi usada ao gravar o arquivo.

6
Aaron Digulla

Bem, seu editor de texto favorito não percebe que \xc3\xa1 devem ser literais de caracteres, mas os interpreta como texto. É por isso que você obtém as barras duplas invertidas na última linha - agora é uma verdadeira barra invertida + xc3, etc. em seu arquivo.

Se você quiser ler e escrever arquivos codificados em Python, é melhor usar o módulo codecs .

Colar texto entre o terminal e os aplicativos é difícil, porque você não sabe qual programa irá interpretar seu texto usando qual codificação. Você poderia tentar o seguinte:

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

Em seguida, cole essa string no seu editor e certifique-se de armazená-lo usando o Latin-1. Sob a suposição de que a área de transferência não interfere na cadeia, a ida e volta deve funcionar.

5
Torsten Marek

A sequência\x .. é algo específico do Python. Não é uma sequência universal de escape de bytes.

Como você realmente entra em ASCII não codificado em UTF-8 depende do seu SO e/ou do seu editor. Veja como você faz no Windows . Para o OS X entrar a com um acento agudo, você pode simplesmente apertar option + E, então Ae quase todos os editores de texto no OS X suportam UTF-8.

4
ʞɔıu

Você também pode melhorar a função open() original para trabalhar com arquivos Unicode, substituindo-a no lugar, usando a função partial. A beleza desta solução é que você não precisa alterar nenhum código antigo. É transparente.

import codecs
import functools
open = functools.partial(codecs.open, encoding='utf-8')
3
hipertracker

Eu estava tentando analisar iCal usando o Python 2.7.9:

de icalendar import Calendar

Mas eu estava ficando:

 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)

e foi corrigido com apenas:

print "{}".format(e[attr].encode("utf-8"))

(Agora ele pode imprimir como se fosse.)

1
Alexx Roche