Python, Unicode e o console do Windows

146

Quando tento imprimir uma seqüência de caracteres Unicode em um console do Windows, recebo um UnicodeEncodeError: 'charmap' codec can't encode character ....erro. Suponho que isso ocorre porque o console do Windows não aceita caracteres somente Unicode. Qual é a melhor maneira de contornar isso? Existe alguma maneira de fazer o Python imprimir automaticamente um em ?vez de falhar nessa situação?

Edit: Estou usando o Python 2.5.


Nota: a resposta @ LasseV.Karlsen com a marca de seleção está desatualizada (a partir de 2008). Por favor, use as soluções / respostas / sugestões abaixo com cuidado !!

A resposta @JFSebastian é mais relevante a partir de hoje (6 de janeiro de 2016).

James Sulak
fonte
Em qual versão do Python você está? Eu vi referências de que isso foi quebrado no 2.4.3 e corrigido no 2.4.4.
Stu
3
relacionado: bugs.python.org/issue1602
jfs
verifique isso .
Soorena 18/09/16
1
a resposta mais simples que eu encontrei é digitar: chcp 65001 antes de usar pyhton em cmd
Soorena
1
Então você deve mudar a sua resposta aceita ...
Mr_and_Mrs_D

Respostas:

38

Nota: Esta resposta está desatualizada (a partir de 2008). Por favor, use a solução abaixo com cuidado !!


Aqui está uma página que detalha o problema e uma solução (procure na página o texto Encapsulando sys.stdout em uma instância ):

PrintFails - Python Wiki

Aqui está um trecho de código dessa página:

$ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line'
  UTF-8
  <type 'unicode'> 2
  Б
  Б

  $ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line' | cat
  None
  <type 'unicode'> 2
  Б
  Б

Há mais informações nessa página, vale a pena ler.

Lasse V. Karlsen
fonte
7
O link está morto e a essência da resposta não foi citada. -1
0xC0000022L
1
Quando tento o conselho dado sobre a embalagem sys.stdout, ele imprime as coisas erradas. Por exemplo, u'\u2013'torna-se em ûvez de um traço.
User2357112 suporta Monica
@ user2357112 Você terá que postar uma nova pergunta sobre isso. O Unicode e o console do sistema não são necessariamente a melhor combinação, mas eu não sei o suficiente sobre isso, portanto, se você precisar de uma resposta definitiva, poste uma pergunta aqui no SO sobre isso.
Lasse V. Karlsen
2
o link está morto. O exemplo de código está incorreto para o console do Windows, onde a página de códigos (OEM), cp437diferente da página de códigos do Windows ANSI, como cp1252. O código não corrige o UnicodeEncodeError: 'charmap' codec can't encode charactererro e pode levar ao mojibake, por exemplo, ا©é silenciosamente substituído por ╪º⌐.
JFS
73

Atualização: Python 3.6 implementa PEP 528: Altere a codificação do console do Windows para UTF-8 : o console padrão no Windows agora aceitará todos os caracteres Unicode. Internamente, ele usa a mesma API Unicode que o win-unicode-consolepacote mencionado abaixo . print(unicode_string)deve funcionar agora.


Eu recebo um UnicodeEncodeError: 'charmap' codec can't encode character... erro.

O erro significa que os caracteres Unicode que você está tentando imprimir não podem ser representados usando a chcpcodificação de caracteres do console atual ( ). A página de código geralmente é uma codificação de 8 bits, cp437que pode representar apenas ~ 0x100 caracteres a partir de ~ 1M caracteres Unicode:

>>> u "\ N {EURO SIGN}". encode ('cp437')
Traceback (última chamada mais recente):
...
UnicodeEncodeError: o codec 'charmap' não pode codificar o caractere '\ u20ac' na posição 0:
mapas de caracteres para 

Suponho que isso ocorre porque o console do Windows não aceita caracteres somente Unicode. Qual é a melhor maneira de contornar isso?

O console do Windows aceita caracteres Unicode e pode até exibi-los (somente BMP) se a fonte correspondente estiver configurada . WriteConsoleW()A API deve ser usada conforme sugerido na resposta de @Daira Hopwood . Pode ser chamado de forma transparente, ou seja, você não precisa e não deve modificar seus scripts se usar o win-unicode-consolepacote :

T:\> py -mpip install win-unicode-console
T:\> py -mrun your_script.py

Veja Qual é o problema do Python 3.4, Unicode, diferentes idiomas e Windows?

Existe alguma maneira de fazer o Python imprimir automaticamente um em ?vez de falhar nessa situação?

Se for suficiente substituir todos os caracteres não codificáveis ​​por ?no seu caso, você poderá definir PYTHONIOENCODINGenvvar :

T:\> set PYTHONIOENCODING=:replace
T:\> python3 -c "print(u'[\N{EURO SIGN}]')"
[?]

No Python 3.6+, a codificação especificada por PYTHONIOENCODINGenvvar é ignorada para buffers de console interativos, a menos que PYTHONLEGACYWINDOWSIOENCODINGenvvar seja definido como uma seqüência de caracteres não vazia.

jfs
fonte
3
"o console padrão no Windows agora aceita todos os caracteres Unicode" MAS você precisa configurar o console: clique com o botão direito do mouse na parte superior das janelas (do cmd ou do python IDLE); em padrão / fonte, escolha o "console Lucida". (Japonês e chinês não trabalho para mim, mas eu deveria sobreviver sem ele ...)
JinSnow
2
@ Guillaume: a resposta contém a frase em negrito no console do Windows: "se a fonte correspondente estiver configurada". Essa resposta não menciona IDLE, mas você não precisa configurar a fonte em que (eu vejo caracteres japoneses e chineses muito bem em IDLE por padrão. Tente print('\u4E01'), print('\u6b63')).
JFS
2
@ Guillaume Você pode até aprender chinês se instalar o pacote de idiomas no Windows 10. Ele adicionou fontes de console compatíveis com chinês.
precisa
28

Apesar das outras respostas plausíveis que sugerem alterar a página de códigos para 65001, isso não funciona . (Além disso, alterar a codificação padrão usando nãosys.setdefaultencoding é uma boa ideia .)

Veja esta pergunta para detalhes e código que funciona.

Daira Hopwood
fonte
2
win-unicode-consolePacote Python (com base em seu código) permite evitar modificar seu script se ele imprime Unicode diretamente usando py -mrun your_script.pycomandos .
JFS
12

Se você não está interessado em obter uma representação confiável do (s) caractere (s) incorreto (s), use algo assim (trabalhando com python> = 2.6, incluindo 3.x):

from __future__ import print_function
import sys

def safeprint(s):
    try:
        print(s)
    except UnicodeEncodeError:
        if sys.version_info >= (3,):
            print(s.encode('utf8').decode(sys.stdout.encoding))
        else:
            print(s.encode('utf8'))

safeprint(u"\N{EM DASH}")

Os caracteres incorretos na string serão convertidos em uma representação que pode ser impressa pelo console do Windows.

Giampaolo Rodolà
fonte
.encode('utf8').decode(sys.stdout.encoding)leva a mojibake por exemplo, u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs
Simplesmente print(s.encode('utf-8'))pode ser uma maneira melhor de evitar erros do compilador. Em vez disso, você obtém a saída \ xNN para caracteres não imprimíveis, o suficiente para minhas mensagens de diagnóstico.
COD-REaD
4
Isso é enormemente, espetacularmente errado. A codificação para UTF-8 e a decodificação como um conjunto de caracteres de 8 bits a) geralmente falham, nem todas as páginas de código têm caracteres para todos os valores de 256 bytes eb) sempre a interpretação incorreta dos dados, produzindo uma bagunça Mojibake .
Martijn Pieters
10

O código abaixo fará a saída do Python para o console como UTF-8, mesmo no Windows.

O console exibirá os caracteres bem no Windows 7, mas no Windows XP não os exibirá bem, mas pelo menos funcionará e o mais importante: você terá uma saída consistente do seu script em todas as plataformas. Você poderá redirecionar a saída para um arquivo.

O código abaixo foi testado com o Python 2.6 no Windows.


#!/usr/bin/python
# -*- coding: UTF-8 -*-

import codecs, sys

reload(sys)
sys.setdefaultencoding('utf-8')

print sys.getdefaultencoding()

if sys.platform == 'win32':
    try:
        import win32console 
    except:
        print "Python Win32 Extensions module is required.\n You can download it from https://sourceforge.net/projects/pywin32/ (x86 and x64 builds are available)\n"
        exit(-1)
    # win32console implementation  of SetConsoleCP does not return a value
    # CP_UTF8 = 65001
    win32console.SetConsoleCP(65001)
    if (win32console.GetConsoleCP() != 65001):
        raise Exception ("Cannot set console codepage to 65001 (UTF-8)")
    win32console.SetConsoleOutputCP(65001)
    if (win32console.GetConsoleOutputCP() != 65001):
        raise Exception ("Cannot set console output codepage to 65001 (UTF-8)")

#import sys, codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
sys.stderr = codecs.getwriter('utf8')(sys.stderr)

print "This is an Е乂αmp١ȅ testing Unicode support using Arabic, Latin, Cyrillic, Greek, Hebrew and CJK code points.\n"
sorin
fonte
1
Existe uma maneira de evitar isso usando apenas um console diferente?
Endolith
@sorin: Por que você primeiro import win32consolefora de um trye depois você faz isso condicionalmente dentro de um try? Não é esse tipo de inútil (o primeiro import)
0xC0000022L
Por que vale a pena, o fornecido por David-Sarah Hopwood funciona (eu não recebi um presente até mesmo correr porque eu não ter incomodado a instalação do módulo extensões Win32)
Jaykul
4
Não altere a codificação padrão do sistema; corrija seus valores Unicode. Alterar a codificação padrão pode interromper as bibliotecas que dependem do comportamento padrão . Há uma razão para você forçar a recarga de um módulo antes de fazer isso.
Martijn Pieters
7

Basta digitar este código na linha de comando antes de executar o script python:

chcp 65001 & set PYTHONIOENCODING=utf-8
c97
fonte
5

Como a resposta de Giampaolo Rodolà, mas ainda mais suja: eu realmente pretendo passar muito tempo (em breve) compreendendo todo o assunto das codificações e como elas se aplicam aos consoles Windoze,

No momento, eu só queria o sthg, o que significaria que o meu programa não iria entrar em colapso e o que eu entendi ... e também que não envolvia a importação de muitos módulos exóticos (em particular eu estou usando o Jython, na metade do tempo um Python acaba por não estar disponível).

def pr(s):
    try:
        print(s)
    except UnicodeEncodeError:
        for c in s:
            try:
                print( c, end='')
            except UnicodeEncodeError:
                print( '?', end='')

NB "pr" é mais curto para digitar do que "print" (e um pouco mais curto para digitar que "safeprint") ...!

microfone roedor
fonte
Inteligente, uma maneira rápida e suja de contornar o problema. Eu acho que isso é ótimo para uma solução intermitente.
JFA 01/07
3

Para o Python 2, tente:

print unicode(string, 'unicode-escape')

Para o Python 3, tente:

import os
string = "002 Could've Would've Should've"
os.system('echo ' + string)

Ou tente win-unicode-console:

pip install win-unicode-console
py -mrun your_script.py
shubaly
fonte
2

TL; DR:

print(yourstring.encode('ascii','replace'));

Eu me deparei com isso sozinho, trabalhando em um bot do Twitch chat (IRC). (Python 2.7 mais recente)

Eu queria analisar as mensagens de bate-papo para responder ...

msg = s.recv(1024).decode("utf-8")

mas também imprima-os com segurança no console em um formato legível por humanos:

print(msg.encode('ascii','replace'));

Isso corrigiu o problema dos UnicodeEncodeError: 'charmap'erros de lançamento do bot e substituiu os caracteres unicode por ?.

Matthew Estock
fonte
2

A causa do seu problema NÃO é o console do Win que não está disposto a aceitar Unicode (como faz isso desde que eu acho que o Win2k por padrão). É a codificação padrão do sistema. Experimente este código e veja o que ele oferece:

import sys
sys.getdefaultencoding()

se ele diz ascii, aí está sua causa ;-) Você precisa criar um arquivo chamado sitecustomize.py e colocá-lo no caminho do python (eu o coloco em /usr/lib/python2.5/site-packages, mas isso é diferente em Win - é c: \ python \ lib \ site-packages ou algo assim), com o seguinte conteúdo:

import sys
sys.setdefaultencoding('utf-8')

e talvez você também queira especificar a codificação em seus arquivos:

# -*- coding: UTF-8 -*-
import sys,time

Edit: mais informações podem ser encontradas no excelente livro Dive into Python

Bartosz Radaczyński
fonte
2
setdefaultencoding () não é mais um sys (a partir da versão 2.0, de acordo com os documentos do módulo).
Jon gaiola
Não posso provar isso agora, mas sei que usei esse truque em uma versão posterior - 2.5 no Windows.
Bartosz Radaczyński 9/04/09
6
OK, depois de um bom tempo, descobri que: "Esta função destina-se apenas a ser usada pela implementação do módulo de site e, quando necessário, por customização de site. Uma vez usada pelo módulo de site, ela é removida do espaço de nome do módulo sys. "
Bartosz Radaczyński
4
na verdade, você pode definir o console do Windows como utf-8. você precisa dizer chcp 65001 e será unicode.
Bartosz Radaczyński
4
Para deixar isso absolutamente claro: é uma péssima idéia alterar a codificação padrão. Isso é o mesmo que andar com a perna quebrada e andar como se nada tivesse acontecido, em vez de pedir a um médico que apareça adequadamente. Todo o código que manipula o texto Unicode deve fazê-lo consistentemente, em vez de confiar na codificação / decodificação implícita.
Martijn Pieters
1

Tipo de parente na resposta de JF Sebastian, mas mais direto.

Se você estiver tendo esse problema ao imprimir no console / terminal, faça o seguinte:

>set PYTHONIOENCODING=UTF-8
Kinjal Dixit
fonte
3
set PYTHONIOENCODING=UTF-8pode levar ao mojibake se o console usar uma codificação diferente, como cp437. cp65001tem vários problemas . Para imprimir Unicode no console do Windows, a API Unicode deve ser usada ( WriteConsoleW()), como sugerido na minha resposta, onde PYTHONIOENCODINGé usada apenas para substituir caracteres que não podem ser representados na página de código OEM atual por ?( WriteConsoleW()funciona mesmo para esses caracteres). PYTHONIOENCODINGpode ser usado se a saída for redirecionada para um arquivo.
JFS
1

Python 3.6 windows7: Há várias maneiras de iniciar um python: você pode usar o console python (que possui um logotipo python) ou o console do windows (está escrito cmd.exe).

Não consegui imprimir caracteres utf8 no console do Windows. A impressão de caracteres utf-8 me gera este erro:

OSError: [winError 87] The paraneter is incorrect 
Exception ignored in: (_io-TextIOwrapper name='(stdout)' mode='w' ' encoding='utf8') 
OSError: [WinError 87] The parameter is incorrect 

Depois de tentar e não entender a resposta acima, descobri que era apenas um problema de configuração. Clique com o botão direito do mouse na parte superior das janelas do console do cmd e, na guia, fontescolha lucida console.

J. Does
fonte
0

James Sulak perguntou:

Existe alguma maneira de fazer o Python imprimir automaticamente um? em vez de falhar nesta situação?

Outras soluções recomendam que tentemos modificar o ambiente do Windows ou substituir a print()função do Python . A resposta abaixo se aproxima mais do cumprimento da solicitação de Sulak.

No Windows 7, o Python 3.5 pode ser criado para imprimir Unicode sem gerar a UnicodeEncodeErrorseguinte:

    No lugar de:     print(text)
    substitute:     print(str(text).encode('utf-8'))

Em vez de lançar uma exceção, o Python agora exibe caracteres Unicode não imprimíveis como códigos hexadecimais \ xNN , por exemplo:

  \ N \ xe2 \ x80 \ x99 \ xc3 \ xa9tait mais do que hal \ xe2 \ x80 \ x99un point noir

Ao invés de

  Halmalo n'était plus qu'un point noir

É verdade que o último é preferível ceteris paribus , mas, caso contrário, o primeiro é completamente preciso para mensagens de diagnóstico. Como exibe Unicode como valores de bytes literais, o primeiro também pode ajudar no diagnóstico de problemas de codificação / decodificação.

Nota: A str()chamada acima é necessária porque, de outra forma, encode()faz com que o Python rejeite um caractere Unicode como uma tupla de números.

COD-READ
fonte