Установка правильной кодировки при конвейеризации stdout в Python

при конвейерном выводе программы Python интерпретатор Python запутывается в кодировке и устанавливает ее в значение None. Это означает такую программу:

# -*- coding: utf-8 -*-
print u"åäö"

будет работать нормально, когда работает нормально, но не с:

UnicodeEncodeError: кодек 'ascii' не может кодировать символ u 'xa0 ' в позиции 0: порядковый номер не в диапазоне (128)

при использовании в последовательности трубы.

что самый лучший путь сделать эту работу пронзая? Могу ли я просто сказать ему использовать любую кодировку оболочки/файловой системы/что бы она ни использовала?

предложения, которые я видел до сих пор, - это изменить ваш site.py непосредственно, или hardcoding defaultencoding, используя этот хак:

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print u"åäö"

есть ли лучший способ сделать работу трубопроводов?

10 ответов


ваш код работает при запуске в скрипте, потому что Python кодирует вывод в любую кодировку, используемую вашим терминальным приложением. Если вы трубите, вы должны закодировать его сами.

эмпирическое правило: Всегда используйте Unicode внутри. Расшифруйте то, что вы получаете, и зашифруйте то, что вы посылаете.

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

другим дидактическим примером является программа Python для преобразования между ISO-8859-1 и UTF-8, делая все прописные буквы между ними.

import sys
for line in sys.stdin:
    # Decode what you receive:
    line = line.decode('iso8859-1')

    # Work with Unicode internally:
    line = line.upper()

    # Encode what you send:
    line = line.encode('utf-8')
    sys.stdout.write(line)

установка системная кодировка по умолчанию-плохая идея, потому что некоторые используемые модули и библиотеки могут полагаться на то, что это ASCII. Не делай этого.


во-первых, относительно этого решения:

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

нецелесообразно явно печатать с заданной кодировкой каждый раз. Это было бы повторяющимся и подверженным ошибкам.

лучшее решение-изменить sys.stdout в начале вашей программы, для кодирования с выбранной кодировкой. Вот одно решение, которое я нашел на Python: как sys.стандартный вывод.кодировка выбрана?, в частности, комментарий "тока":

import sys
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)

вы можете попробовать изменить переменную среды "PYTHONIOENCODING" на " utf_8."Я написал страница о моем испытании с этой проблемой.

Tl; dr сообщения в блоге:

import sys, locale, os
print(sys.stdout.encoding)
print(sys.stdout.isatty())
print(locale.getpreferredencoding())
print(sys.getfilesystemencoding())
print(os.environ["PYTHONIOENCODING"])
print(chr(246), chr(9786), chr(9787))

дает

utf_8
False
ANSI_X3.4-1968
ascii
utf_8
ö ☺ ☻

export PYTHONIOENCODING=utf-8

сделайте работу, но не можете установить ее на самом python ...

что мы можем сделать, это проверить, если это не настройка и сказать пользователю установить его перед сценарием вызова с:

if __name__ == '__main__':
    if (sys.stdout.encoding is None):
        print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout."
        exit(1)

обновление для ответа на комментарий: проблема просто существует при подключении к stdout . Я тестировал в Fedora 25 Python 2.7.13

python --version
Python 2.7.13

cat b.py

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

print sys.stdout.encoding

работает ./б.пы

UTF-8

работает ./b.py / less

None

Я аналогичный вопрос на прошлой неделе. Это было легко исправить в моей IDE (PyCharm).

вот мое исправление:

начиная с строки меню PyCharm: Файл - > Настройки... - >Редактор - > кодировки файлов, затем установите: "кодировка IDE", "кодировка проекта" и "кодировка по умолчанию для файлов свойств" все в UTF-8, и теперь она работает как шарм.

надеюсь, что это помогает!


спорная санированная версия ответа Крейга Маккуина.

import sys, codecs
class EncodedOut:
    def __init__(self, enc):
        self.enc = enc
        self.stdout = sys.stdout
    def __enter__(self):
        if sys.stdout.encoding is None:
            w = codecs.getwriter(self.enc)
            sys.stdout = w(sys.stdout)
    def __exit__(self, exc_ty, exc_val, tb):
        sys.stdout = self.stdout

использование:

with EncodedOut('utf-8'):
    print u'ÅÄÖåäö'

Я мог бы "автоматизировать" его с вызовом:

def __fix_io_encoding(last_resort_default='UTF-8'):
  import sys
  if [x for x in (sys.stdin,sys.stdout,sys.stderr) if x.encoding is None] :
      import os
      defEnc = None
      if defEnc is None :
        try:
          import locale
          defEnc = locale.getpreferredencoding()
        except: pass
      if defEnc is None :
        try: defEnc = sys.getfilesystemencoding()
        except: pass
      if defEnc is None :
        try: defEnc = sys.stdin.encoding
        except: pass
      if defEnc is None :
        defEnc = last_resort_default
      os.environ['PYTHONIOENCODING'] = os.environ.get("PYTHONIOENCODING",defEnc)
      os.execvpe(sys.argv[0],sys.argv,os.environ)
__fix_io_encoding() ; del __fix_io_encoding

Да, здесь можно получить бесконечный цикл, если этот" setenv " терпит неудачу.


NB: я использую языка Jython в частности, v 2.7, поэтому, возможно, это не относится к CPython...

NB2: первые две строки моего .файл py вот:

# -*- coding: utf-8 -*-
from __future__ import print_function

механизм построения строки "%" (он же "оператор интерполяции") также вызывает дополнительные проблемы... Если кодировка по умолчанию для "среды" - ASCII, и вы пытаетесь сделать что-то вроде

print( "bonjour, %s" % "fréd" )  # Call this "print A"

у вас не будет проблем с запуском в Eclipse... В Windows CLI (DOS window) вы обнаружите, что кодировка кодовая страница 850 (моя ОС Windows 7) или что-то подобное, которое может обрабатывать европейские акцентированные символы, по крайней мере, так это сработает.

print( u"bonjour, %s" % "fréd" ) # Call this "print B"

также будет работать.

если, OTOH, вы направляетесь в файл из CLI, кодировка stdout будет None, которая по умолчанию будет ASCII (в моей ОС в любом случае), которая не сможет обрабатывать ни один из вышеперечисленных принтов... (страшная ошибка кодирования).

таким образом, вы можете подумать о перенаправлении stdout с помощью

sys.stdout = codecs.getwriter('utf8')(sys.stdout)

и попробуйте запустить в трубопроводе CLI в файл... Очень странно, что печать выше будет работать... Но печать B выше выбросит ошибку кодирования! Однако следующее будет работать нормально:

print( u"bonjour, " + "fréd" ) # Call this "print C"

вывод, к которому я пришел (предварительно), заключается в том, что если строка, указанная как Unicode строка, использующая префикс "u", передается механизму обработки%, который, как представляется, включает использование кодировки среды по умолчанию,независимо от того, установили ли вы stdout для перенаправления!

как люди справляются с этим-это вопрос выбора. Я бы добро пожаловать эксперт Unicode, чтобы сказать, почему это происходит, есть ли у меня это неправильно в некотором роде, какое предпочтительное решение для этого, применяется ли оно также к CPython, произошло ли это в Python 3, и т. д., п.


на Ubuntu 12.10 и gnome Terminal ошибка не возникает, когда программа печатается в stdout или подключается к трубе для других программ. И кодировка файла, и кодировка терминала - UTF-8.

$ cat a.py
# -*- coding: utf-8 -*-
print "åäö"
$ python a.py
åäö
$ python a.py | tee out
åäö

какую ОС и эмулятор терминала вы используете? Я слышал, что некоторые из моих коллег имеют аналогичные проблемы при использовании iTerm 2 и OS X; виновником может быть iTerm 2.

Update: этот ответ неправильный - см. комментарии для подробности


я столкнулся с этой проблемой в устаревшей программе, и было трудно определить, где что было напечатано. Я помог себе с этим Хак:

# encoding_utf8.py
import codecs
import builtins


def print_utf8(text, **kwargs):
    print(str(text).encode('utf-8'), **kwargs)


def print_utf8(fn):
    def print_fn(*args, **kwargs):
        return fn(str(*args).encode('utf-8'), **kwargs)
    return print_fn


builtins.print = print_utf8(print)

поверх моего сценария, test.py:

import encoding_utf8
string = 'Axwell Λ Ingrosso'
print(string)

обратите внимание, что это изменяет все вызовы для печати, чтобы использовать кодировку, поэтому ваша консоль напечатает это:

$ python test.py
b'Axwell \xce\x9b Ingrosso'