it-swarm.dev

Mudando uma codificação padrão do Python?

Eu tenho muitos "não pode codificar" e "não pode decodificar" problemas com Python quando eu executar meus aplicativos a partir do console. Mas no EclipsePyDev IDE, a codificação de caracteres padrão é definida como UTF-8 , e estou bem.

Eu pesquisei por definir a codificação padrão, e as pessoas dizem que o Python exclui a função sys.setdefaultencoding na inicialização e não podemos usá-la.

Então, qual é a melhor solução para isso?

117
Ali Nadalizadeh

Aqui está um método mais simples (hack) que lhe devolve a função setdefaultencoding() que foi deletada de sys:

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

Isso não é uma coisa segura para fazer, entretanto: isso é obviamente um hack, já que sys.setdefaultencoding() é propositadamente removido de sys quando o Python é iniciado. Reativá-lo e alterar a codificação padrão pode quebrar o código que depende de ASCII ser o padrão (esse código pode ser de terceiros, o que geralmente tornaria a correção impossível ou perigosa).

134
Eric O Lebigot

Se você receber este erro ao tentar redirecionar/redirecionar a saída do seu script

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

Basta exportar PYTHONIOENCODING no console e depois executar seu código.

export PYTHONIOENCODING=utf8

53
iman

A) Para controlar a saída sys.getdefaultencoding():

python -c 'import sys; print(sys.getdefaultencoding())'

asciiname__

Então

echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py

PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

Você poderia colocar seu sitecustomize.py mais alto no seu PYTHONPATHname__.

Você também pode gostar de tentar reload(sys).setdefaultencoding by @EOL 

B) Para controlar stdin.encoding e stdout.encoding você deseja definir PYTHONIOENCODINGname__:

python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

Então 

PYTHONIOENCODING="utf-16-be" python -c 'import sys; 
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

Finalmente: você pode usar A) ou B) ou ambos!

46
lukmdo

Começando com PyDev 3.4.1, a codificação padrão não está mais sendo alterada. Veja este ticket para detalhes.

Para versões anteriores, uma solução é garantir que o PyDev não seja executado com o UTF-8 como a codificação padrão. No Eclipse, execute as configurações de diálogo ("executar configurações", se bem me lembro); você pode escolher a codificação padrão na guia comum. Altere-o para US-ASCII se quiser que esses erros sejam "antecipados" (em outras palavras: no seu ambiente PyDev). Veja também uma postagem do blog original para esta solução alternativa .

17
ChristopheD

Em relação ao python2 (e python2 apenas), algumas das respostas anteriores dependem do uso do seguinte hack:

import sys
reload(sys)  # Reload is a hack
sys.setdefaultencoding('UTF8')

É desencorajado usá-lo (marque this ou this )

No meu caso, ele vem com um efeito colateral: estou usando notebooks ipython e, depois de executar o código, a função 'print' não funciona mais. Eu acho que haveria solução para isso, mas ainda acho que usar o hack não deve ser a opção correta.

Depois de tentar muitas opções, a que funcionou para mim foi usando o mesmo código no sitecustomize.py, onde esse pedaço de código deve ser. Depois de avaliar esse módulo, a função setdefaultencoding é removida do sys.

Então a solução é acrescentar ao arquivo /usr/lib/python2.7/sitecustomize.py o código:

import sys
sys.setdefaultencoding('UTF8')

Quando eu uso o virtualenvwrapper, o arquivo que eu edito é ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py.

E quando eu uso com cadernos python e conda, é ~/anaconda2/lib/python2.7/sitecustomize.py

12
kiril

Há um blog perspicaz sobre isso.

Veja https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/ .

Eu parafraseio seu conteúdo abaixo.

No python 2, que não era tão fortemente digitado em relação à codificação de strings, você poderia executar operações em strings codificadas de forma diferente e ter sucesso. Por exemplo. o seguinte retornaria True.

u'Toshio' == 'Toshio'

Isso seria válido para todas as sequências (normais, não definidas) que foram codificadas em sys.getdefaultencoding(), cujo padrão era ascii, mas não para outras.

A codificação padrão era para ser alterada em todo o sistema em site.py, mas não em outro lugar. Os hacks (também apresentados aqui) para configurá-lo em módulos de usuário eram apenas isso: hacks, não a solução.

O Python 3 alterou a codificação do sistema para padrão para utf-8 (quando LC_CTYPE é compatível com unicode), mas o problema fundamental foi resolvido com o requisito de codificar explicitamente cadeias "byte" sempre que elas são usadas com strings unicode.

7
ibotty

Primeiro: reload(sys) e definir alguma codificação padrão aleatória apenas em relação à necessidade de um fluxo de terminal de saída é uma prática ruim. reload frequentemente muda coisas em sys que foram postas em prática dependendo do ambiente - por exemplo streams sys.stdin/stdout, sys.excepthook, etc. 

Resolvendo o problema de codificação no stdout

A melhor solução que eu conheço para resolver o problema de codificação de print 'ing unicode strings e beyond-ascii str' s (por exemplo, de literais) em sys.stdout é: para cuidar de um sys.stdout (objeto semelhante a arquivo) que é capaz e opcionalmente tolerante em relação às necessidades:

  • Quando sys.stdout.encoding for None por algum motivo, ou inexistente, ou erroneamente falso ou "menos" do que o terminal ou fluxo stdout realmente é capaz, tente fornecer um atributo .encoding correto. Finalmente, substituindo sys.stdout & sys.stderr por um objeto semelhante a um arquivo de tradução.

  • Quando o terminal/stream ainda não consegue codificar todos os caracteres unicode que estão ocorrendo, e quando você não quer quebrar o print por causa disso, você pode introduzir um comportamento de codificação com substituição no objeto semelhante a um arquivo de tradução.

Aqui um exemplo:

#!/usr/bin/env python
# encoding: utf-8
import sys

class SmartStdout:
    def __init__(self, encoding=None, org_stdout=None):
        if org_stdout is None:
            org_stdout = getattr(sys.stdout, 'org_stdout', sys.stdout)
        self.org_stdout = org_stdout
        self.encoding = encoding or \
                        getattr(org_stdout, 'encoding', None) or 'utf-8'
    def write(self, s):
        self.org_stdout.write(s.encode(self.encoding, 'backslashreplace'))
    def __getattr__(self, name):
        return getattr(self.org_stdout, name)

if __== '__main__':
    if sys.stdout.isatty():
        sys.stdout = sys.stderr = SmartStdout()

    us = u'aouäöüфżß²'
    print us
    sys.stdout.flush()

Usando literais de string simples além-ascii no código Python 2/2 + 3

O único bom motivo para alterar a codificação padrão global (para UTF-8 somente) eu acho que é referente a um aplicativo código fonte decisão - e não por causa de codificações de fluxos de E/S: Para gravar literais de strings beyond-ascii código sem ser forçado a usar sempre escape unicode u'string' style. Isso pode ser feito de forma bastante consistente (apesar do que o artigo de anonbadger diz) cuidando de uma base de código Python 2 ou Python 2 + 3 que usa literais de string simples ascii ou UTF-8 consistentemente - até onde seqüências de caracteres potencialmente passam por conversão silenciosa unicode e mover entre módulos ou potencialmente ir para stdout. Para isso, prefira "# encoding: utf-8" ou ascii (sem declaração). Altere ou derrube bibliotecas que ainda dependam de uma maneira muito burra fatalmente em erros de codificação padrão ascii além do chr # 127 (o que é raro hoje em dia).

E faça assim no início do aplicativo (e/ou via sitecustomize.py) além do esquema SmartStdout acima - sem usar reload(sys):

...
def set_defaultencoding_globally(encoding='utf-8'):
    assert sys.getdefaultencoding() in ('ascii', 'mbcs', encoding)
    import imp
    _sys_org = imp.load_dynamic('_sys_org', 'sys')
    _sys_org.setdefaultencoding(encoding)

if __== '__main__':
    sys.stdout = sys.stderr = SmartStdout()
    set_defaultencoding_globally('utf-8') 
    s = 'aouäöüфżß²'
    print s

Desta forma, os literais de string e a maioria das operações (exceto a iteração de caracteres) funcionam sem pensar na conversão de unicode como se houvesse apenas Python3. O File I/O, claro, sempre precisa de um cuidado especial com relação às codificações - como no Python3.

Nota: as strings plains são implicitamente convertidas de utf-8 para unicode em SmartStdout antes de serem convertidas para o enconding do fluxo de saída.

2
kxr

Isso resolveu o problema para mim.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"
2
twasbrillig

Este é um truque rápido para qualquer um que esteja (1) Em uma plataforma Windows (2) rodando o Python 2.7 e (3) chateado porque um belo software (isto é, não escrito por você, portanto não é candidato a impressão de codificação/decodificação) manobras) não exibirá os "caracteres muito unicode" no ambiente IDLE (Pythonwin imprime unicode fine), por exemplo, os símbolos lógicos de primeira ordem que Stephan Boyer usa na saída de seu testador pedagógico em First Logic Prover .

Eu não gostei da idéia de forçar um recarregamento sys e eu não consegui fazer o sistema cooperar com as variáveis ​​de ambiente de configuração como PYTHONIOENCODING (tentei a variável de ambiente do Windows direto e também soltei isso em um sitecustomize.py em pacotes de sites como um liner = 'utf-8').

Portanto, se você estiver disposto a abrir caminho para o sucesso, acesse seu diretório IDLE, normalmente: "C:\Python27\Lib\idlelib" Localize o arquivo IOBinding.py. Faça uma cópia desse arquivo e armazene-o em outro lugar para poder reverter ao comportamento original quando você escolher. Abra o arquivo no idlelib com um editor (por exemplo, IDLE). Vá para esta área de código:

# Encoding for file names
filesystemencoding = sys.getfilesystemencoding()

encoding = "ascii"
if sys.platform == 'win32':
    # On Windows, we could use "mbcs". However, to give the user
    # a portable encoding name, we need to find the code page 
    try:
        # --> 6/5/17 hack to force IDLE to display utf-8 rather than cp1252
        # --> encoding = locale.getdefaultlocale()[1]
        encoding = 'utf-8'
        codecs.lookup(encoding)
    except LookupError:
        pass

Em outras palavras, comente a linha de código original seguindo o 'try' que estava tornando a variável de codificação igual a locale.getdefaultlocale (porque isso lhe dará cp1252 que você não quer) e força bruta para 'utf-8' (adicionando a linha 'encoding =' utf-8 'como mostrado). 

Acredito que isso afeta apenas a exibição IDLE para stdout e não a codificação usada para nomes de arquivos etc. (obtida na codificação do sistema de arquivos anterior). Se você tiver um problema com qualquer outro código executado em IDLE posteriormente, basta substituir o arquivo IOBinding.py pelo arquivo original não modificado.

0
Dalton Bentley

Aqui está a abordagem que eu usei para produzir código que fosse compatível tanto com python2 quanto com python3 e sempre produzisse a saída utf8. Eu encontrei esta resposta em outro lugar, mas não me lembro da fonte.

Essa abordagem funciona substituindo sys.stdout por algo que não é exatamente semelhante a um arquivo (mas ainda usa apenas coisas na biblioteca padrão). Isso pode causar problemas para suas bibliotecas subjacentes, mas no caso simples em que você tem um bom controle sobre como o sys.stdout out é usado através de sua estrutura, isso pode ser uma abordagem razoável.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
0
Att Righ