Leitura de caracteres de arquivo em Python

102

Em um arquivo de texto, há uma string "Não gosto disso".

No entanto, quando leio em uma string, torna-se "I don \ xe2 \ x80 \ x98t assim". Eu entendo que \ u2018 é a representação unicode de "'". eu uso

f1 = open (file1, "r")
text = f1.read()

comando para fazer a leitura.

Agora, é possível ler a string de tal forma que, quando for lida na string, seja "Não gosto disso", em vez de "Não \ xe2 \ x80 \ x98t assim"?

Segunda edição: vi algumas pessoas usarem o mapeamento para resolver esse problema, mas realmente, não há nenhuma conversão embutida que faça esse tipo de conversão ANSI para Unicode (e vice-versa)?

Graviton
fonte
Alguns comentários: Já vi algumas pessoas usarem o mapeamento para resolver esse problema, mas realmente, não há nenhuma conversão embutida que faça esse tipo de conversão ANSI para Unicode (e vice-versa)? Obrigado!
Graviton
Não existe, porque existem centenas de milhares de pontos de código Unicode. Como você decidiria qual deveria ser mapeado para quais caracteres ASCII?
John Millikin,
2
btw, seu arquivo de texto está quebrado! U + 2018 é a "MARCA DE COTAÇÃO ÚNICA ESQUERDA", não um apóstrofo (U + 0027 mais comumente).
john, seu comentário está errado, pelo menos no sentido geral. a lib iconv pode ser usada para transliterar caracteres Unicode para ascii (mesmo dependendo do local. $ python -c 'print u "\ u2018" .encode ("utf-8")' | iconv -t 'ascii // translit' | xxd 0000000: 270a
o fato é que você precisa converter UNICODE em ASCII (não o contrário).
hasen

Respostas:

157

Ref: http://docs.python.org/howto/unicode

Ler Unicode de um arquivo é, portanto, simples:

import codecs
with codecs.open('unicode.rst', encoding='utf-8') as f:
    for line in f:
        print repr(line)

Também é possível abrir arquivos em modo de atualização, permitindo leitura e gravação:

with codecs.open('test', encoding='utf-8', mode='w+') as f:
    f.write(u'\u4500 blah blah blah\n')
    f.seek(0)
    print repr(f.readline()[:1])

EDIT : Estou assumindo que seu objetivo pretendido é apenas ser capaz de ler o arquivo corretamente em uma string em Python. Se você está tentando converter para uma string ASCII de Unicode, não há realmente uma maneira direta de fazer isso, já que os caracteres Unicode não existem necessariamente em ASCII.

Se você estiver tentando converter para uma string ASCII, tente uma das seguintes opções:

  1. Substitua os caracteres Unicode específicos por equivalentes ASCII, se você estiver procurando apenas para lidar com alguns casos especiais, como este exemplo particular

  2. Use o unicodedatamódulo normalize()e o string.encode()método para converter da melhor forma possível para o próximo equivalente ASCII mais próximo (Ref https://web.archive.org/web/20090228203858/http://techxplorer.com/2006/07/18/converting- unicode-to-ascii-using-python ):

    >>> teststr
    u'I don\xe2\x80\x98t like this'
    >>> unicodedata.normalize('NFKD', teststr).encode('ascii', 'ignore')
    'I donat like this'
Jay
fonte
3
codecsmódulo não controla o modo de nova linha universal corretamente. Em io.open()vez disso, use no Python 2.7+ (é integrado open()no Python 3).
jfs
15

Existem alguns pontos a serem considerados.

Um caractere \ u2018 pode aparecer apenas como um fragmento de representação de uma string Unicode em Python, por exemplo, se você escrever:

>>> text = u'‘'
>>> print repr(text)
u'\u2018'

Agora, se você simplesmente deseja imprimir a string Unicode de maneira bonita, basta usar o encodemétodo Unicode :

>>> text = u'I don\u2018t like this'
>>> print text.encode('utf-8')
I dont like this

Para garantir que todas as linhas de qualquer arquivo sejam lidas como unicode, é melhor usar a codecs.openfunção em vez de apenas open, que permite especificar a codificação do arquivo:

>>> import codecs
>>> f1 = codecs.open(file1, "r", "utf-8")
>>> text = f1.read()
>>> print type(text)
<type 'unicode'>
>>> print text.encode('utf-8')
I dont like this
DzinX
fonte
6

Mas é realmente "Eu não gosto disso" e não "Eu não gosto disso". O caractere u '\ u2018' é um caractere completamente diferente de "'" (e, visualmente, deve corresponder mais a' `').

Se você está tentando converter Unicode codificado em ASCII simples, talvez possa manter um mapeamento de pontuação Unicode que gostaria de traduzir para ASCII.

punctuation = {
  u'\u2018': "'",
  u'\u2019': "'",
}
for src, dest in punctuation.iteritems():
  text = text.replace(src, dest)

Há uma grande quantidade de caracteres de pontuação em Unicode , no entanto, mas suponho que você possa contar com apenas alguns deles sendo realmente usados ​​por qualquer aplicativo que esteja criando os documentos que você está lendo.

Logan
fonte
1
na verdade, se você fizer o dict mapear ordinais Unicode para Unicode ordinais ({0x2018: 0x27, 0x2019: 0x27}), você pode simplesmente passar todo o dict para text.translate () para fazer toda a substituição de uma vez.
Thomas Wouters
5

Também é possível ler um arquivo de texto codificado usando o método de leitura python 3:

f = open (file.txt, 'r', encoding='utf-8')
text = f.read()
f.close()

Com esta variação, não há necessidade de importar nenhuma biblioteca adicional

Stein
fonte
3

Deixando de lado o fato de que seu arquivo de texto está corrompido (U + 2018 é uma aspa esquerda, não um apóstrofo): iconv pode ser usado para transliterar caracteres Unicode para ascii.

Você terá que procurar por "iconvcodec" no Google, já que o módulo parece não ser mais compatível e não consigo encontrar uma página inicial canônica para ele.

>>> import iconvcodec
>>> from locale import setlocale, LC_ALL
>>> setlocale(LC_ALL, '')
>>> u'\u2018'.encode('ascii//translit')
"'"

Como alternativa, você pode usar o iconvutilitário de linha de comando para limpar seu arquivo:

$ xxd foo
0000000: e280 980a                                ....
$ iconv -t 'ascii//translit' foo | xxd
0000000: 270a                                     '.

fonte
2

Existe a possibilidade de que de alguma forma você tenha uma string não Unicode com caracteres de escape Unicode, por exemplo:

>>> print repr(text)
'I don\\u2018t like this'

Isso realmente aconteceu comigo uma vez antes. Você pode usar um unicode_escapecodec para decodificar a string para unicode e, em seguida, codificá-la para qualquer formato que desejar:

>>> uni = text.decode('unicode_escape')
>>> print type(uni)
<type 'unicode'>
>>> print uni.encode('utf-8')
I dont like this
DzinX
fonte
1

Esta é a maneira do Pythons mostrar strings codificadas em Unicode. Mas acho que você deve conseguir imprimir a string na tela ou gravá-la em um novo arquivo sem problemas.

>>> test = u"I don\u2018t like this"
>>> test
u'I don\u2018t like this'
>>> print test
I dont like this
xardias
fonte
1

Na verdade, U + 2018 é a representação Unicode do caractere especial '. Se desejar, você pode converter instâncias desse caractere em U + 0027 com este código:

text = text.replace (u"\u2018", "'")

Além disso, o que você está usando para gravar o arquivo? f1.read()deve retornar uma string parecida com esta:

'I don\xe2\x80\x98t like this'

Se estiver retornando esta string, o arquivo está sendo escrito incorretamente:

'I don\u2018t like this'
John Millikin
fonte
Desculpe! Como você disse, ele está retornando 'Eu não \ xe2 \ x80 \ x98t assim'
Graviton
O 'Eu não \ xe2 \ x80 \ x98t assim' que você está vendo é o que o Python chamaria de str. Parece ser a codificação utf-8 de u'Eu não gosto disso ', que é uma instância Unicode em Python. Tente chamar .decode ('utf-8') no primeiro ou .encode ('utf-8') no último.
Logan,
@hop: oops, esqueci ord () retorna decimal em vez de hex. Obrigado pela captura.
John Millikin,