Установка правильной кодировки при передаче stdout в Python
при передаче вывода программы 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"åäö"
есть ли лучший способ заставить трубопроводы работать?
10 ответов:
ваш код работает при запуске в скрипте, потому что Python кодирует вывод в любую кодировку, которую использует ваше терминальное приложение. Если вы трубите, вы должны закодировать его самостоятельно.
эмпирическое правило: Всегда используйте Unicode внутренне. Декодируйте то, что вы получаете, и кодируйте то, что вы посылаете.
# -*- coding: utf-8 -*- print u"åäö".encode('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.стандартный вывод.кодировка выбрана?, в частности комментарий "toka":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кошка б.пы
#!/usr/bin/env python #-*- coding: utf-8 -*- import sys print sys.stdout.encodingработает ./б.пы
UTF-8работает. /b.py / меньше
None
Я аналогичная проблема на прошлой неделе. Это было легко исправить в моей IDE (PyCharm).
вот мое исправление:
начиная с панели меню PyCharm: File - > Settings... - >Редактор - > кодировки файлов, затем установите: "кодировка 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...
особое примечание 2: первые две строки мои .файл py вот они:
# -*- coding: utf-8 -*- from __future__ import print_functionмеханизм построения строки "%" (он же "оператор интерполяции") также вызывает дополнительные проблемы... Если кодировка по умолчанию "среды" является 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, вы направляетесь к файлу из CLI, кодировка stdout будет None, которая по умолчанию будет ASCII (в моей ОС в любом случае), которая не сможет обрабатывать ни один из вышеупомянутых отпечатков... (страшная ошибка кодирования).
тогда вы можете подумать о перенаправлении вашего stdout с помощью
sys.stdout = codecs.getwriter('utf8')(sys.stdout)и попробуйте запустить в трубопроводе CLI к файлу... Очень странно, что печать выше будет работать... Но печать в выше выбросит ошибку кодирования! Однако следующее будет работать нормально:
print( u"bonjour, " + "fréd" ) # Call this "print C"вывод, к которому я пришел (условно), заключается в том, что если строка, которая указана как Unicode строка, использующая префикс "u", передается в механизм %-обработки, который, как представляется, включает использование кодировки среды по умолчанию,независимо от того, установили ли вы stdout для перенаправления!
как люди справляются с этим-это вопрос выбора. Я бы добро пожаловать эксперт Unicode, чтобы сказать, почему это происходит, не ошибся ли я в чем-то, какое предпочтительное решение для этого, применяется ли оно также к CPython, произошло ли это в Python 3, и т. д., прием.
на 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 может быть виновником.
обновление: этот ответ неверен - см. Комментарий к подробности
я столкнулся с этой проблемой в устаревшей программе, и было трудно определить, где что было напечатано. Я помог себе с этим Хак:
# 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'
Comments