3-байтовые символы Python декодируются с помощью символов, отличных от ascii, в CGI-скрипте - PullRequest
0 голосов
/ 01 июня 2018

У меня очень короткий пример кода:

print("Content-Type: text/plain; charset=utf-8")
print("Access-Control-Allow-Origin: *")
print()

x = 'Chloë'.encode()
print(x)
print(x.decode())

Обратите внимание на non Ascii ë , который является источником всех проблем.

Вызов скрипта в bashиспользование python3 ./test.py приводит к следующему (правильному) вводу:

Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: *

b'Chlo\xc3\xab'
Chloë

Однако, вызывая его из браузера, последняя строка отсутствует (заголовки, конечно, не видны, но они присутствуют).Таким образом, единственная видимая часть:

b'Chlo\xc3\xab'

Знаете ли вы, где может быть проблема?

1 Ответ

0 голосов
/ 01 июня 2018

Вы печатаете Unicode на дескриптор sys.stdout (который является файловым объектом по умолчанию, print() записывает в).Затем этот объект должен снова закодировать ваши данные, но он должен делать это в зависимости от среды, к которой он подключен.

Когда вы запускаете python3 ./test.py, тогда вы подключаетесь к своему терминалу или консоли, и этообычно настраивается на указание сценариям, какой кодек подходит.В системах POSIX (Linux, Mac) вы можете запустить команду locale, чтобы увидеть, что это за конфигурация.В вашей локали консоли нет проблем с отображением не ASCII-кода, такого как ë.

Но при запуске в качестве CGI-скрипта, подключенного к веб-серверу, такая языковая конфигурация отсутствует, и Python почти наверняка имеетвместо этого вернулся к наименьшему общему знаменателю: ASCII.И в этом случае попытка распечатать текст не в Юникоде приведет к исключению:

$ LC_ALL="en_US.UTF-8" python3 -c "print(b'Chlo\xc3\xab'.decode())"
Chloë
$ LC_ALL="C" python3 -c "print(b'Chlo\xc3\xab'.decode())"  # C => "no locale set"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\xeb' in position 4: ordinal not in range(128)

Поскольку исключение имеет место только после создания заголовков и всех других выходных данных,Вы не видите код ошибки HTTP.Однако исключение должно было быть зарегистрировано в журналах ошибок сервера.

Если ваш сценарий должен выводить UTF-8 в браузер, как настроено в заголовке Content-Type, который вы отправляете, replace sys.stdout для принудительной установки этого кодека:

import sys
from io import TextIOWrapper

sys.stdout = TextIOWrapper(sys.stdout.buffer.detach(), encoding='utf8')

В Python 3 текстовые файлы, подобные тем, которые используются для потока sys.stdout, содержат объект буфера, который, в свою очередь, содержит объект двоичного файла, который заботитсяфактической записи двоичных данных.Действительно, объект внешнего текстового файла отвечает только за кодирование при записи.Вышеуказанный объект заменяет этот внешний объект другим, который всегда кодируется в UTF-8.

...