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

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

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

будет нормально работать при нормальной работе, но с ошибкой:

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

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

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

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

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

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

Ответы

Ответ 1

Ваш код работает при запуске в script, потому что 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. Не делайте этого.

Ответ 2

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

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

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

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

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

Ответ 3

Вы можете попробовать изменить переменную среды "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
ö ☺ ☻

Ответ 4

export PYTHONIOENCODING=utf-8

выполнить задание, но не может установить его на сам python...

что мы можем сделать, это проверить, не задано ли значение и сообщить пользователю установить его перед вызовом script с помощью

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)

Обновить, чтобы ответить на комментарий: проблема существует только при переходе на стандартный вывод. Я тестировал в 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

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

UTF-8

работает./b.py | меньше

None

Ответ 5

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

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

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

Надеюсь, это поможет!

Ответ 6

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

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'ÅÄÖåäö'

Ответ 7

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

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" терпит неудачу.

Ответ 8

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

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

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

Обновление: этот ответ неверен - подробнее см. комментарии

Ответ 9

Я просто подумал, что я упомянул кое-что здесь, с которым мне пришлось долго экспериментировать, прежде чем я наконец понял, что происходит. Это может быть настолько очевидным для всех здесь, что они не удосужились упомянуть об этом. Но это помогло бы мне, если бы они, так по этому принципу...!

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

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

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

Механизм построения строки "%" (AKA "интерполяционный" ) вызывает ДОПОЛНИТЕЛЬНЫЕ проблемы... Если кодировка по умолчанию "среда" - это ASCII, и вы пытаетесь сделать что-то вроде

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

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

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

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

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

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

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

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

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

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

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

Ответ 10

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

# 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'