Установка правильной кодировки при конвейеризации 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'