При запуске кода:
#! /usr/bin/env python
# -*- coding: UTF-8 -*-
import xml.etree.ElementTree as ET
print ET.fromstring('<?xml version = "1.0" encoding = "UTF-8" standalone = "yes"?><root><road>vägen</road></root>').find('road').text
Производит ожидаемый вывод vägen, однако, если передать это по конвейеру wc -l, я получаю UnicodeEncodeError, например. (TEerr.py содержит фрагмент кода, приведенный выше):
:~> ETerr.py | wc -l
Traceback (most recent call last):
File "./ETerr.py", line 5, in <module>
print ET.fromstring('<?xml version = "1.0" encoding = "UTF-8" standalone = "yes"?><root><road>vägen</road></root>').find('road').text
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 1: ordinal not in range(128)
0
:~>
Как может код вести себя по-другому, если его вывод передан по конвейеру или нет, и как я могу исправить это, чтобы это не было.
Обратите внимание, что приведенный выше фрагмент кода просто настроен для демонстрации проблемы с минимальным количеством кода, в реальном сценарии, где мне нужно решить проблему, xml извлекается с помощью urllib, поэтому я мало могу контролировать его формат.
В любом случае: когда вы печатаете строку Unicode в Python 2, она использует кодировку по умолчанию. Вероятно, он угадывает значение по умолчанию UTF-8 или Latin1, когда stdout является терминалом, и ASCII, когда это канал. В обоих случаях попробуйте распечатать sys.getdefaultencoding(). Если это проблема, покажите нам, какие у вас переменные среды с префиксом LOCALE и LC_.
добавить .encode('utf-8') в конец оператора печати
Если вы знаете, что ваш терминал и инструменты ожидают определенной кодировки, и вы просто хотите заставить свой код использовать эту кодировку, несмотря ни на что, не заботясь о переносимости на другие системы, вы можете print u.encode(‘utf-8’). Но было бы лучше диагностировать и решать актуальные проблемы с конфигурацией.
Кроме того, есть ли причина для этого использовать Python 2? Поскольку кодировка stdio - одна из вещей, улучшенных в Python 3 и улучшенных еще несколько раз в Python 3.7, и это, скорее всего, вообще не появится.
Это чистая проблема Python, как показано в трассировке стека, команда в конвейере теперь ничего не говорит о следующей команде и не заботится об этом.
@abarnert, да, это утверждение print не работает. sys.getdefaultencoding() дает ascii независимо от того, подключен ли выход по трубопроводу или нет
@eagle, конечно, это решает проблему, но на самом деле не объясняет, почему поведение отличается, когда вывод передается по конвейеру или нет
Еще одно исключение: я предполагаю, что вы работаете в системе POSIX (Linux, Mac или другой * BSD), а не в Windows; я прав? (Даже с Cygwin Python все становится сложнее в Windows, поэтому я хочу убедиться, что мы можем проигнорировать это здесь.)






Во-первых, позвольте мне указать, что это не проблема в Python 3, и ее исправление на самом деле является одной из причин, по которым вообще стоило изменить язык, нарушающий совместимость. Но я предполагаю, что у вас есть веская причина для использования Python 2, и вы не можете просто обновить его.
Ближайшая причина здесь (при условии, что вы используете Python 2.7 на платформе POSIX - все может быть сложнее в старых версиях 2.x и Windows) - это значение sys.stdout.encoding. Когда вы запускаете интерпретатор, он выполняет эквивалент этого псевдокода:
if isatty(stdoutfd):
sys.stdout.encoding = parse_locale(os.environ('LC_CTYPE'))
else:
sys.stdout.encoding = None
И каждый раз, когда вы записываете write в файл, включая sys.stdout, в том числе неявно из оператора print, он делает что-то вроде этого:
if isinstance(s, unicode):
if self.encoding:
s = s.encode(self.encoding)
else:
s = s.encode(sys.getdefaultencoding())
Фактический код выполняет стандартные функции POSIX, ища резервные варианты, такие как LANG, и жестко кодирует откат к UTF-8 в некоторых случаях для Mac OS X и т. д., Но это достаточно близко.
Это очень редко задокументировано в разделе file.encoding:
The encoding that this file uses. When Unicode strings are written to a file, they will be converted to byte strings using this encoding. In addition, when the file is connected to a terminal, the attribute gives the encoding that the terminal is likely to use (that information might be incorrect if the user has misconfigured the terminal). The attribute is read-only and may not be present on all file-like objects. It may also be
None, in which case the file uses the system default encoding for converting Unicode strings.
Чтобы убедиться, что это ваша проблема, попробуйте следующее:
$ python -c 'print __import__("sys").stdout.encoding'
UTF-8
$ python -c 'print __import__("sys").stdout.encoding' | cat
None
Чтобы быть более уверенным, что проблема в этом:
$ PYTHONIOENCODING=Latin-1 python -c 'print __import__("sys").stdout.encoding'
Latin-1
$ PYTHONIOENCODING=Latin-1 python -c 'print __import__("sys").stdout.encoding' | cat
Latin-1
Итак, как это исправить?
Что ж, очевидный способ - перейти на Python 3.6, где вы получите UTF-8 в обоих случаях, но я предполагаю, что есть причина, по которой вы используете Python 2.7 и не можете легко его изменить.
Правильное решение на самом деле довольно сложно. Но если вам нужно быстрое и грязное решение, которое работает для вашей системы и для большинства современных систем Linux и Mac со стандартными настройками Python 2.7 (даже если это может быть катастрофически неправильным для старых систем Linux, старых версий Python 2.x и странных настроек) , вы также можете:
PYTHONIOENCODING, чтобы переопределить обнаружение и принудительно использовать UTF-8. Возможно, стоит установить это в вашем profile или аналогичном, если вы знаете, что каждый терминал и каждый инструмент, который вы когда-либо собираетесь использовать из этой учетной записи, - это UTF-8, хотя это ужасная идея, если это не так.sys.stdout.encoding и оберните его кодировкой 'UTF-8', если это None..encode('UTF-8') на всем, что вы печатаете.Вы правы, я должен был добавить, что это не проблема в python3 и что проблема проявляется на платформе POSIX. Интересно, однако, какой смысл устанавливать кодировку stdout в зависимости от того, является ли это tty-подобным устройством или нет?
@cpaitor Я предполагаю, что исходное обоснование имело смысл для большинства систем Unix 90-х годов (где языковой стандарт терминала вполне мог быть полностью отделен от того, что ожидалось в большинстве текстовых файлов - если это вообще было что-то полезное с самого начала), и что все попытки придумать что-то более подходящее для современного Linux имели слишком много проблем с обратной совместимостью, чтобы реализовать их до версии 3.0, но я не знаю наверняка. (OS X упростила это, просто указав, что терминал, все инструменты пользовательского пространства и все текстовые файлы имеют кодировку UTF-8, точка…)
@cpaitor Также: дистрибутивы linux постепенно преобразовывались в "все по умолчанию UTF-8" примерно в то же время, когда они начинали миграцию на Python 3. Так что, вероятно, имело смысл потратить больше энергии на то, чтобы убедиться, что Python 3 работает в их дистрибутиве из коробки, чем настраивать пользовательские конфигурации Python 2 для их пакетов дистрибутива. (Тем более, что по крайней мере у Canonical и Redhat были штатные разработчики Python, работающие над миграцией Python 3.)
Попробуйте разбить это на части: одна хранит xml в виде строки, одна шляпа выполняет анализ ET, другая выполняет поиск и, наконец, третья выполняет печать. наверное - последний из них, который терпит неудачу, но знать наверняка полезно.