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

StackOverflow https://stackoverflow.com/questions/492483

Вопрос

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

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

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

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

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

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

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

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

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

Это было полезно?

Решение

Ваш код работает при запуске в скрипте, потому что 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 в начале вашей программы для кодирования используйте выбранную кодировку.Вот одно из решений, которое я нашел на Питон:Как выбирается sys.stdout.encoding?, в частности, комментарий "тока":

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

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

Tl; dr из записи в блоге:

дает вам

utf_8
False
ANSI_X3.4-1968
ascii
utf_8
ö ☺ ☻
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

кошка b.py

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

print sys.stdout.encoding

бегущий ./b.py

UTF-8

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

None

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

Вот было мое решение:

Начиная со строки меню PyCharm:Файл -> Настройки...-> Редактор -> Кодировки файлов, затем установите:"IDE Encoding", "Project Encoding" и "Default encoding for properties files" переведены в 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" завершится неудачей.

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

ПРИМЕЧАНИЕ:Я использую Джитон в частности, версия 2.7, так что, возможно, это может не относиться к CPython - процессор...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Обновить:Этот ответ неверен - подробности смотрите в комментариях

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

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

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

$ python test.py
b'Axwell \xce\x9b Ingrosso'
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top