Alterando a codificação padrão do Python?


143

Eu tenho muitos problemas de "não é possível codificar" e "não é possível decodificar" com o Python quando executo meus aplicativos no console. Mas no Eclipse PyDev IDE, a codificação de caracteres padrão é definida como UTF-8 , e eu estou bem.

Eu procurei ao redor para definir a codificação padrão, e as pessoas dizem que o Python exclui a sys.setdefaultencodingfunção na inicialização, e não podemos usá-la.

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


1
Veja a postagem do blog The Illusive setdefaultencoding .
djc 16/02/10

3
The best solution is to learn to use encode and decode correctly instead of using hacks.Isso certamente foi possível com o python2 ao custo de sempre lembrar-se de fazê-lo / de forma consistente usando sua própria interface. Minha experiência sugere que isso se torna altamente problemático quando você está escrevendo um código com o qual deseja trabalhar com python2 e python3.
Att Righ

Respostas:


159

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

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

(Nota para o Python 3.4+: reload()está na importlibbiblioteca.)

Porém, isso não é uma coisa segura a se fazer : obviamente é um hack, pois sys.setdefaultencoding()é propositadamente removido de sysquando o Python é iniciado. A reativação e a alteração da codificação padrão podem interromper o código que depende do padrão ASCII (esse código pode ser de terceiros, o que geralmente tornaria a correção impossível ou perigosa).


5
Fiz uma votação baixa, porque essa resposta não ajuda na execução de aplicativos existentes (que é uma maneira de interpretar a pergunta), está errada quando você está escrevendo / mantém um aplicativo e perigosa ao escrever uma biblioteca. O caminho certo é definir LC_CTYPE(ou em um aplicativo, verifique se está definido corretamente e aborte com uma mensagem de erro significativa).
ibotty

@ibotty Concordo que esta resposta é um hack e que é perigoso usá-la. Porém, ele responde à pergunta ("Alterando a codificação padrão do Python?"). Você tem uma referência sobre o efeito da variável de ambiente LC_CTYPE no interpretador Python?
Eric O Lebigot

bem, não mencionou, é um hack no começo. fora isso, respostas perigosas que carecem de menção alguma são inúteis.
ibotty

1
@EOL você está certo. No LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
entanto

1
@ user2394901 O uso de sys.setdefaultencoding () sempre foi desencorajado !! E a codificação do py3k é conectada ao "utf-8" e sua alteração gera um erro.
Marlon Abeykoon

70

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

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

Apenas exporte PYTHONIOENCODING no console e execute seu código.

export PYTHONIOENCODING=utf8


3
Esta é a única solução que fez alguma diferença para mim. - Estou no Debian 7, com configurações de localidade quebradas. Obrigado.
Pryo

4
Defina LC_CTYPEcomo algo sensato. Faz todos os outros programas felizes também.
ibotty

5
Um erro maior no Python3 é que esse PYTHONIOENCODING=utf8não é o padrão. Isso faz com que scripts de quebrar só porqueLC_ALL=C
Tino

Set LC_CTYPE to something sensible insteadEsta é uma sugestão razoável. Isso não funciona tão bem quando você está tentando distribuir código que funciona apenas no sistema de outra pessoa.
Att Righ

Os sistemas operacionais Debian e Redhat usam um C.utf8código de idioma para fornecer que a C. glibc upstream mais sensata esteja trabalhando para adicioná-lo, então talvez não devamos culpar o Python por respeitar as configurações de código de idioma \…?
Arthur2e5

52

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

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

ascii

Então

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

e

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

utf-16-be

Você pode colocar seu sitecustomize.py mais alto no seu PYTHONPATH.

Você também pode tentar reload(sys).setdefaultencodingpor @EOL

B) Para controlar stdin.encodinge stdout.encodingvocê deseja definir PYTHONIOENCODING:

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!


(python2 apenas) separar mas interessante é que se prolonga acima com from __future__ import unicode_literalsver discussão
lukmdo

17

A partir do PyDev 3.4.1, a codificação padrão não está mais sendo alterada. Veja este ticket para mais detalhes.

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


1
Obrigado Chris. Especialmente considerando o comentário de Mark T acima, sua resposta parece ser a mais apropriada para mim. E para alguém que não é principalmente um usuário do Eclipse / PyDev, eu nunca teria descoberto isso sozinho.
30511 Sean

Gostaria de mudar isso globalmente (em vez de uma vez por configuração de execução), mas ainda não entendi como - pediram um q separado: stackoverflow.com/questions/9394277/…
Tim Diggins

13

Em relação ao python2 (e somente python2), algumas das respostas anteriores contam com o seguinte hack:

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

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

No meu caso, ele vem com um efeito colateral: estou usando notebooks ipython e, depois que executo o código, a função 'print' não funciona mais. 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 estava usando o mesmo código no sitecustomize.py, onde esse trecho de código deve estar . Após avaliar esse módulo, a função setdefaultencoding é removida do sys.

Portanto, a solução é anexar ao arquivo /usr/lib/python2.7/sitecustomize.pyo 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 notebooks python e conda, é ~/anaconda2/lib/python2.7/sitecustomize.py


8

Há uma publicação perspicaz no blog sobre isso.

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

Parafrasco seu conteúdo abaixo.

No python 2, que não foi tão fortemente digitado em relação à codificação de cadeias, você pode executar operações em cadeias codificadas de maneira diferente e obter êxito. Por exemplo, o seguinte retornaria True.

u'Toshio' == 'Toshio'

Isso seria válido para todas as strings (normais, sem prefixo) codificadas sys.getdefaultencoding(), que eram padronizadas ascii, mas não para outras.

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

O Python 3 alterou a codificação do sistema para o padrão utf-8 (quando LC_CTYPE reconhece o unicode), mas o problema fundamental foi resolvido com o requisito de codificar explicitamente cadeias de "bytes" sempre que usadas com cadeias de caracteres unicode.


4

Primeiro: reload(sys)configurar uma codificação padrão aleatória apenas para a necessidade de um fluxo de terminal de saída é uma prática recomendada.reloadgeralmente muda as coisas no sys que foram implementadas dependendo do ambiente - por exemplo, sys.stdin / stdout streams, 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 printstrings unicode e além de ascii str(por exemplo, de literais) em sys.stdout é: cuidar de um sys.stdout (objeto semelhante a arquivo) que seja capaz e opcionalmente tolerante em relação às necessidades:

  • Quando sys.stdout.encodingé Nonepor algum motivo, ou não-existente, ou erroneamente falsa ou "menos" do que o que o terminal stdout ou transmitir realmente é capaz de, em seguida, tentar fornecer uma correta .encodingatributo. Por fim, substituindo sys.stdout & sys.stderrpor um objeto semelhante a arquivo de tradução.

  • Quando o terminal / fluxo ainda não pode codificar todos os caracteres unicode ocorrentes, e quando você não deseja interromper printapenas por causa disso, é possível introduzir um comportamento de codificar com substituir no objeto de conversão de arquivo.

Aqui está 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 __name__ == '__main__':
    if sys.stdout.isatty():
        sys.stdout = sys.stderr = SmartStdout()

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

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

A única boa razão para alterar a codificação padrão global (apenas para UTF-8) é uma decisão sobre o código-fonte do aplicativo - e não por causa de problemas de codificação de fluxo de E / S: Para escrever literais de cadeia além do ascii no código sem ser forçado para sempre usar u'string'escape unicode de estilo. Isso pode ser feito de maneira bastante consistente (apesar do que o artigo de anonbadger diz), cuidando de uma base de código-fonte Python 2 ou Python 2 + 3 que usa consistentemente literais de cadeia simples ascii ou UTF-8 - na medida em que essas cadeias potencialmente sofrem silêncio conversão unicode e mova-se entre módulos ou, potencialmente, vá para stdout. Para isso, prefira "# encoding: utf-8"ou ascii (sem declaração). Altere ou descarte bibliotecas que ainda dependem de maneira muito idiota fatalmente de erros de codificação padrão ascii além da chr # 127 (que hoje é rara hoje).

E faça isso no início do aplicativo (e / ou no site sitecustomize.py), além do SmartStdoutesquema 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 __name__ == '__main__':
    sys.stdout = sys.stderr = SmartStdout()
    set_defaultencoding_globally('utf-8') 
    s = 'aouäöüфżß²'
    print s

Dessa maneira, literais de string e a maioria das operações (exceto a iteração de caracteres) funcionam confortavelmente sem pensar na conversão unicode como se houvesse apenas Python3. É claro que as E / S de arquivos sempre precisam de cuidados especiais com relação às codificações - como no Python3.

Nota: as sequências de planícies são convertidas implicitamente de utf-8 em unicode SmartStdoutantes de serem convertidas no encconding do fluxo de saída.


4

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

Essa abordagem funciona substituindo sys.stdoutpor algo que não é muito parecido com 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 na sua estrutura, essa pode ser uma abordagem razoável.

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

3

Isso corrigiu o problema para mim.

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

1

Este é um truque rápido para quem está (1) em uma plataforma Windows (2) executando o Python 2.7 e (3) irritado porque um bom software (ou seja, não foi escrito por você, portanto não é candidato imediato à impressão de codificação / decodificação) manobras) não exibirá os "caracteres unicode bonitos" no ambiente IDLE (Pythonwin imprime muito bem o unicode). Por exemplo, os símbolos lógicos da First Order Logic que Stephan Boyer usa na saída de seu provador pedagógico no First Order Logic Prover .

Eu não gostei da idéia de forçar o recarregamento do sistema e não consegui que o sistema cooperasse com a definição de variáveis ​​de ambiente como PYTHONIOENCODING (tentei a variável de ambiente direta do Windows e também a soltei em sitecustomize.py nos pacotes de sites como um liner = 'utf-8').

Portanto, se você deseja abrir o caminho para o sucesso, vá para o 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 local para que você possa voltar ao comportamento original quando desejar. 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 após a ' tentativa ' que estava tornando a variável de codificação igual a locale.getdefaultlocale (porque isso fornecerá o cp1252 que você não deseja) e, em vez disso, force-a para 'utf-8 '(adicionando a linha' encoding = 'utf-8 ' como mostrado).

Eu acredito que isso afeta apenas a exibição IDLE para stdout e não a codificação usada para nomes de arquivos etc. (que é obtida anteriormente na codificação do sistema de arquivos). Se você tiver algum problema com qualquer outro código executado no IDLE posteriormente, substitua o arquivo IOBinding.py pelo arquivo original não modificado.


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.