[python-brasil] Problema com encoding de string

1,338 views
Skip to first unread message

André Filipe de Assunção e Brito

unread,
Jun 6, 2009, 7:42:26 PM6/6/09
to python...@yahoogrupos.com.br
Ae galera!

To com o seguinte problema. Estou lendo um arquivo texto, onde na
primeira linha, tem uma string. Essa string tá em um formato q eu não
reconheço, mas o vim reconhece na boa e mostra a bendita da 'ç'
(cedilha).
Já tentei usar os métodos de encode do objeto string (str.encode), e
tentei com os encodings cp850, iso8859-1 e utf-8. Tb já tentei setar o
encoding no inicio do script e necas.

Alguem ai já passou por isso?

Valeu!

--
André Filipe de Assunção e Brito
Graduando em Oceanografia
Universidade Federal do Paraná


------------------------------------

,-----------------------------------------------------------.
| Antes de enviar um e-mail para o grupo leia: |
| http://www.pythonbrasil.com.br/moin.cgi/AntesDePerguntar |
| E se você é usuário do BOL lembre-se de cadastrar o |
| e-mail do grupo na lista branca do seu sistema anti-spam. |
`-----------------------------------------------------------´Links do Yahoo! Grupos

<*> Para visitar o site do seu grupo na web, acesse:
http://br.groups.yahoo.com/group/python-brasil/

<*> Para sair deste grupo, envie um e-mail para:
python-brasi...@yahoogrupos.com.br

<*> O uso que você faz do Yahoo! Grupos está sujeito aos:
http://br.yahoo.com/info/utos.html


Danilo Faustinoni Cabello

unread,
Jun 6, 2009, 8:18:09 PM6/6/09
to python...@yahoogrupos.com.br
2009/6/6 André Filipe de Assunção e Brito <de...@ufpr.br>:

> Ae galera!
>
> To com o seguinte problema. Estou lendo um arquivo texto, onde na
> primeira linha, tem uma string. Essa string tá em um formato q eu não
> reconheço, mas o vim reconhece na boa e mostra a bendita da 'ç'
> (cedilha).
> Já tentei usar os métodos de encode do objeto string (str.encode), e
> tentei com os encodings cp850, iso8859-1 e utf-8. Tb já tentei setar o
> encoding no inicio do script e necas.
Já tentou o módulo codecs?

import codecs
arq = codecs.open('arquivo.txt', 'r', 'utf-8')
print arq.read()

Abraço,
Danilo Cabello

Luciano Ramalho

unread,
Jun 6, 2009, 9:19:05 PM6/6/09
to python...@yahoogrupos.com.br
2009/6/6 André Filipe de Assunção e Brito <de...@ufpr.br>:

> To com o seguinte problema. Estou lendo um arquivo texto, onde na
> primeira linha, tem uma string. Essa string tá em um formato q eu não
> reconheço, mas o vim reconhece na boa e mostra a bendita da 'ç'
> (cedilha).

> Já tentei usar os métodos de encode do objeto string (str.encode), e
> tentei com os encodings cp850, iso8859-1 e utf-8. Tb já tentei setar o
> encoding no inicio do script e necas.

Setar o encoding no início do programa-fonte não tem qualquer relação
com o seu problema, mas é uma mandinga que muita gente recomenda. No
caso, acender uma vela para São Jorge pode ser mais eficaz.

Setar o encoding no início do programa-fonte serve única e
exclusivamente para que o Python saiba como interpretar os caracteres
não-ASCII do seu código-fonte. Se não tem nenhum caractere acentuado
no seu código-fonte, é desnecessário setar o encoding, e fazer isso
não vai resolver o problema ao ler um arquivo.

O que faltou você explicar exatamente o que quer fazer com a string
lida. Vai salvar em um banco de dados? Vai exibir no console? No
Linux, no Windows ou no MacOS? Vai exibir em uma GUI? Qual? Vai
embutir em uma página HTML?

Mesmo sem saber exatamente o que você tá querendo fazer, vou tentar ajudar.

O método que você quer usar provavelmente é o decode. Você escreve:

u = s.decode('utf-8')

e assim transforma uma string de bytes s (da classe str) codificada
como UTF-8 para uma string u, da classe unicode.

Uma vez que você tem uma instância de unicode, pode codificá-la para
outro encoding qualquer, produzindo outra string str. Por exemplo:

s2 = u.encode('cp1252')

Agora a string s2 tem os caracteres codificados segundo a tabela
Windows-1252 (ou codepage 1252).

Se quiser mais ajuda, por gentileza nos conte exatamente o que está
tentando fazer com o texto lido do arquivo, e exatamente qual o erro
que está encontrando.

[ ]s
Luciano

Luciano Ramalho

unread,
Jun 6, 2009, 9:21:24 PM6/6/09
to python...@yahoogrupos.com.br
Ah, sim, faltou também dizer qual a versão do Python que está usando.

Praticamente tudo mudou no que se refere a encodings no Python 3, e as
dicas que eu dei são válidas para as versões 2.4 a 2.6.

André Filipe de Assunção e Brito

unread,
Jun 7, 2009, 5:08:44 PM6/7/09
to python...@yahoogrupos.com.br
Ae Luciano

Realmente não falei a finalidade da parada. Estou lendo um arquivo
texto, onde na primeira linha é o nome do local. Em um dos arquivos, o
nome do local tem uma "mardita" de uma cedilha(ç). Não sei o charset
padrão do arquivo. Só sei que quando abro no vim, seja no terminal ou
no gvim, ele mostra o caractere normalmente(e mostra o tal do "modo
DOS", no final). Se tento ler o arquivo usando o less, aparece
(Guaraque<E7>aba), ao invés de "Guaraqueçaba". Estou usando
Gnome-terminal como terminal.

No final, gravo isso em um banco de dados usando sqlalchemy.
Tentei o método q o Danilo explicou num email anterior na mesma thread
e necas. O python tb não conseguiu entender o arquivo. Usei a seguinte
instrução:

list = codecs.open('arquivo', 'r', 'iso8859-1').readline()

só alterando os charsets em iso, utf-8 e cp850 e a saida foi:

u'Guaraque\xfeaba\r\n' para cp850

u'Guaraque\xe7aba\r\n' para iso8859-1

e "'utf8' codec can't decode bytes in position 8-10: invalid data"
quando tentei abrir como utf-8.

Se leio o arquivo normalmente, converto o objeto list para string e
aplico o decode, a saida é a mesma q dos comandos acima.

O fato de eu ler a primeira linha e armazenar como um objeto list pode
afetar o comportamento disso tudo???

Valeu pela ajuda!!!

2009/6/6 Luciano Ramalho <ram...@gmail.com>:

Graduando em Oceanografia
Universidade Federal do Paraná

Luciano Ramalho

unread,
Jun 7, 2009, 7:54:55 PM6/7/09
to python...@yahoogrupos.com.br
2009/6/7 André Filipe de Assunção e Brito <de...@ufpr.br>:

> Realmente não falei a finalidade da parada. Estou lendo um arquivo
> texto, onde na primeira linha é o nome do local. Em um dos arquivos, o
> nome do local tem uma "mardita" de uma cedilha(ç). Não sei o charset
> padrão do arquivo.

Bom, isso é a primeira coisa a determinar né? Não vale a pena tentar
chutar, tem que investigar e descobrir a resposta. Chute só depois que
a investigação concluir que existe mais de uma possibilidade. Antes
não.

> Só sei que quando abro no vim, seja no terminal ou
> no gvim, ele mostra o caractere normalmente(e mostra o tal do "modo
> DOS", no final).

Eu não uso gvim, mas suponho que o lance do "modo DOS" não tem nada a
ver com o encoding dos acentos, e sim com as quebras de linha. O gvim
deve ter notado que elas são CR+LF ou '\n\r', e não apenas '\n' como
se usa no Unix, Linux e MacOS.

> Se tento ler o arquivo usando o less, aparece
> (Guaraque<E7>aba), ao invés de "Guaraqueçaba". Estou usando
> Gnome-terminal como terminal.

Esta é uma excelente dica. Quer dizer que o cedilha está representado
como 'e7'.

> No final, gravo isso em um banco de dados usando sqlalchemy.
> Tentei o método q o Danilo explicou num email anterior na mesma thread
> e necas. O python tb não conseguiu entender o arquivo. Usei a seguinte
> instrução:
>
> list = codecs.open('arquivo', 'r', 'iso8859-1').readline()

Porque você acha que "o python não conseguiu entender o arquivo"?

> só alterando os charsets em iso, utf-8 e cp850 e a saida foi:
>
> u'Guaraque\xfeaba\r\n' para cp850

Você tentou cp850 porque estava chutando antes de investigar. Já olhou
a tabela cp850? Tem na Wikipédia:

http://en.wikipedia.org/wiki/Code_page_850

O caractere na posição E7 não se parece muito com um cedilha...

> u'Guaraque\xe7aba\r\n' para iso8859-1

O que tem de errado nisso? Para mim, está tudo certo.

Talvez você é que não esteja sabendo ler. O prefixo u e as aspas
indicam que esta é uma representação de um objeto unicode, obtida com
a função repr. É o que o console do Python usa para exibir objetos por
default.

O Python 2.x *jamais* exibe carateres não-ascii ao representar objetos
via funçã repr, porque esta função precisa ser capaz de exibir os
dados sempre, independente de encoding, uma vez que sua principal
utilidade é depuração.

Agora, veja este teste que eu fiz no console do meu Python 2.5.2,
rodando no Ubuntu Linux 8.04 com terminal configurado para UTF-8:

>>> s = u'Guaraque\xe7aba\r\n'
>>> s


u'Guaraque\xe7aba\r\n'

>>> print s
Guaraqueçaba

>>>

Uia, parece que está tudo certo, e o Python entendeu direitinho o encoding!

> e "'utf8' codec can't decode bytes in position 8-10: invalid data"
> quando tentei abrir como utf-8.

Se você tivesse olhado a tabela iso8859-1 na Wikipédia, não teria
perdido tempo com utf-8. Por sinal, não poderia ser mesmo UTF-8 porque
nessa codificação todos os caracteres não-ascii ocupam dois bytes, e o
que você tinha no lugar do cedilha era apenas um byte.

Um bom aliado para investigar essas coisas é o xxd, utilitário do
linux que faz um "dump" hexadecimal de qualquer arquivo (para arquivos
grandes, é melhor usar xxd arquivo | less):

$ xxd txt-latin-1.txt
0000000: 4775 6172 6171 7565 e761 6261 0a Guaraque.aba.

Dá para ver que entre o 'e' (65) e o 'a' (61) só tem um byte, o 'ç'
(e7). O código \e7 representa cedilha em duas codificações importantes
no Brasil: iso8859-1 e cp1252 (a segunda contem todos os caracteres da
primeira, e mais alguns).

> Se leio o arquivo normalmente, converto o objeto list para string e
> aplico o decode, a saida é a mesma q dos comandos acima.

A saída está certíssima, é você que não tá sabendo ler a saída, sacou?

Quando se usa o método decode de strings str, ou codec.open, o
resultado é sempre strings unicode (classe unicode). Quando se dá
print o Python tenta usar o encoding do terminal para exibir o
resultado. Em Linuxes modernos, o encoding do terminal é UTF-8, que
permite exibir qualquer caractere Unicode.

Para gravar estes dados no banco de dados, você terá que converter do
unicode para o encoding do banco (a menos que o driver faça isso
automaticamente, mas você não disse que banco está usando).

Por via das dúvidas, em vez de depender de conversão automática, faça
você mesmo a conversão usando o método encode:

>>> s = u'Guaraque\xe7aba'
>>> s
u'Guaraque\xe7aba'
>>> type(s)
<type 'unicode'>
>>> ut = s.encode('utf8')
>>> type(ut)
<type 'str'>
>>> ut
'Guaraque\xc3\xa7aba'
>>> print ut
Guaraqueçaba
>>>

Veja como o encode transforma de unicode para o tipo str. No caso do
encoding utf-8, isso significa que o cedilha vira dois bytes: C3 e A7.

Agora você precisa saber qual é o encoding que deve usar no seu banco
de dados, e pronto.

[ ]s
Luciano

Nilo Menezes

unread,
Jun 8, 2009, 4:27:01 AM6/8/09
to python...@yahoogrupos.com.br
André Filipe de Assunção e Brito escreveu:

> Ae Luciano
>
> Realmente não falei a finalidade da parada. Estou lendo um arquivo
> texto, onde na primeira linha é o nome do local. Em um dos arquivos, o
> nome do local tem uma "mardita" de uma cedilha(ç). Não sei o charset
> padrão do arquivo. Só sei que quando abro no vim, seja no terminal ou
> no gvim, ele mostra o caractere normalmente(e mostra o tal do "modo
> DOS", no final). Se tento ler o arquivo usando o less, aparece
> (Guaraque<E7>aba), ao invés de "Guaraqueçaba". Estou usando
> Gnome-terminal como terminal.
>
>
Tenta cp437 (página DOS para inglês... padrão software velho :-))
[]

Nilo

http://en.wikipedia.org/wiki/Cp437

--

----------------------------------------
Nilo Menezes
----------------------------------------
Blog: http://junglecoders.blogspot.com
Home Page: http://www.nilo.pro.br
Hobby: http://invasores.sourceforge.net
----------------------------------------

Nilo Menezes

unread,
Jun 8, 2009, 4:37:00 AM6/8/09
to python...@yahoogrupos.com.br
Eu uso o BeautifulSoup para trabalhar com HTML e eles utilizam uma
classe para tentar detectar o encoding de um arquivo (primeiro usando o
encoding declarado no html/xml)... navegando na documentação eu achei
isso aqui:

http://chardet.feedparser.org/

Muito útil quando você trabalha com arquivos de encoding desconhecido. É
bom lembar que o que vale mesmo é um dump hexadecimal do arquivo, não se
deixe enganar pelo que aparece na tela, principalmente quando
trabalhamos com outras línguas.

Um utilitário que uso no Linux e no Mac é o hexdump.
hexdump -C arquivo
No Ruindows você pode pedir um hexview do arquivo usando o PSPad.

[]

Nilo

André Filipe de Assunção e Brito

unread,
Jun 8, 2009, 5:31:17 PM6/8/09
to python...@yahoogrupos.com.br
Meu caneco! Q aula!!! Valeu pela força toda Luciano! Lição aprendida!!!

Muito obrigado mesmo!
Aquele abraço

2009/6/7 Luciano Ramalho <ram...@gmail.com>:

Graduando em Oceanografia
Universidade Federal do Paraná

Reply all
Reply to author
Forward
0 new messages