#python #python-3.x #utf-8
Вопрос:
Я использую Python 3.9.1 и Linux (CentOS 7). Я хочу распечатать символы юникода на консоли. Я хочу все делать в UTF-8. Если я открою интерактивную консоль python и напишу:
print("├")
все идет хорошо, и он печатает:
├
Теперь я вставляю ту же строку print("├")
в файл, затем сохраняю файл в кодировке UTF-8 (по умолчанию в Linux). Затем я получаю следующую ошибку:
UnicodeEncodeError: 'latin-1' codec can't encode character 'u251c' in position 0: ordinal not in range(256)
Откуда взялось это «латинское-1»?
Я также заставляю UTF-8 в первой строке (что в любом случае должно быть по умолчанию в Python3)
# coding: utf8
но это ничего не меняет.
Больше информации о том, что работает, а что нет:
s = "├"
#print(s) # FAIL
s2 = s.encode('utf8')
print(s2) # prints b'xe2x94x9c'
print(s2.decode('latin-1')) # prints the right thing
Что здесь происходит? Могу ли я получить такое же поведение в сценарии, как и в интерактивной консоли?
Комментарии:
1. Я полагаю, вы используете Windows?
2. Может показаться, что при запуске вашего сценария он печатается в среде, использующей
latin-1
кодировку? Т. Е. окно консоли, в котором выполняется ваш сценарий, не использует кодировку UTF-8?3. Я не совсем понимаю,
print(s2.decode('latin-1'))
никогда не следует печатать правильные вещи, так как s2 закодирован в utf-8.4. Проверьте значение переменной среды
PYTHONIOENCODING
.5. Для какой
LANG
переменной среды задано значение?
Ответ №1:
s = "├"
(в исходном файле в кодировке UTF-8) присваивает символу u251C
первую позицию s
строки в кодировке UTF-8.
print(s)
не удается, потому что печать здесь связана с отправкой байтов, представляющих s
стандартный вывод, который ожидает latin-1
кодирования. По сути, что-то вроде s.encode('latin-1')
сбоя, так как первый символ в строке не может быть правильно закодирован.
Если вы буквально запустите этот оператор ( s.encode('latin-1')
) вместо этого, вы обнаружите, что он вызывает ту же ошибку.
s2 = s.encode('utf8')
работает просто отлично, он говорит Python явно кодировать содержимое s
в последовательность байтов. s2
теперь содержит байтовую кодировку s
, использующую кодировку UTF-8. (возможно, » b » было бы лучшим именем переменной, в конце концов, это не строка)
print(s2)
действительно печатается b'xe2x94x9c'
, так как он просто печатает представление последовательности байтов на Python. Это не строка, поэтому вы получаете напечатанное представление значения. Как и должно быть, это буквальное значение , которое вы могли бы использовать для определения s2
, т. Е. s2 = b'xe2x94x9c'
Ничего не изменило бы.
print(s2.decode('latin-1'))
печатать правильные вещи-это немного загадка. s2
является ли правильная последовательность байтов UTF-8 для символа U 251C (https://www.fileformat.info/info/unicode/char/251c/index.htm)
По-видимому , ваш Python берет результат s2.decode('latin-1')
, снова кодирует его в latin-1
виде последовательности байтов, которая затем записывается в выходной поток, где она отображается правильно для вас.
Поскольку Python будет делать то же самое для предыдущих операторов печати, пытающихся напечатать строку в кодировке UTF-8, это объясняет, почему они отображаются неправильно (или вообще не отображаются).
Решением было бы явно указать Python переопределить кодировку для стандартного вывода как UTF-8, чтобы вы могли распечатать строку UTF-8 без попыток Python закодировать ее как latin-1
последовательность байтов кодирования (что приведет к ошибке).
Как описано здесь https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING вы можете сделать это, установив SET PYTHONENCODING=UTF-8
. И наоборот, если вы хотите воспроизвести проблему в интерактивной среде, вы, вероятно, сможете добиться такого поведения PYTHONLEGACYWINDOWSSTDIO
.
Где и когда это установить, зависит от вашей системной среды. Полагаются ли другие приложения на более старые сценарии или другие версии Python, которые этого не делают? Если нет, вы можете рассмотреть возможность установки глобальной системной переменной среды. В качестве альтернативы вы можете установить его непосредственно перед выполнением сценария, т. Е. в пакетном файле, в котором он выполняется.
Комментарии:
1. Они очень ясно дали понять, что это среда Linux, так что я не думаю
PYTHONLEGACYWINDOWSSTDIO
, что это будет иметь какое-либо значение.
Ответ №2:
Причина заключалась в том , что моя LANG
переменная окружения была установлена в en_US
значение, тогда как это должно было быть en_US.UTF-8
.
Другой способ решить проблему-установить PYTHONENCODING
значение UTF-8
(для меня оно было пустым).
Я все еще не до конца понимаю, почему Python путается из-за этого только для неинтерактивных сценариев…
Более подробная информация: https://simulrpi.readthedocs.io/en/latest/display_problems.html
Комментарии:
1. Попробуйте
import sys;print(sys.stdin.encoding,sys.stdout.encoding)
использовать исходныеLANG
настройки как в интерактивном приглашении, так и в сценарии, чтобы узнать, какие кодировки Python использует по умолчанию в каждом сценарии. Вы также можетеPYTHONUTF8=1
включить режим UTF-8.