Помогите мне понять, почему Unicode иногда работает только с Python - PullRequest
11 голосов
/ 17 апреля 2011

Вот небольшая программа:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

print('abcd kΩ ☠ °C √Hz µF ü ☃ ♥')  
print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥')

В Ubuntu, терминале Gnome, IPython делает то, что я ожидал:

In [6]: run Unicodetest.py
abcd kΩ ☠ °C √Hz µF ü ☃ ♥
abcd kΩ ☠ °C √Hz µF ü ☃ ♥

Я получаю тот же вывод, если я ввожу команды на trypython.org .

codepad.org , с другой стороны, выдает ошибку для второй команды:

abcd kΩ ☠ °C √Hz µF ü ☃ ♥
Traceback (most recent call last):
  Line 6, in <module>
    print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥')
UnicodeEncodeError: 'ascii' codec can't encode character u'\u03a9' in position 6: ordinal not in range(128)

Наоборот,IDLE в Windows манипулирует выводом первой команды, но не жалуется на вторую:

>>>
abcd kΩ ☠ °C √Hz µF ü ☃ ♥
abcd kΩ ☠ °C √Hz µF ü ☃ ♥

IPython в командной строке Windows или через версию Python (x, y) Console2 оба манипулируютпервый вывод и жалоба на второй:

In [9]: run Unicodetest.py
abcd kΩ ☠ °C √Hz µF ü ☃ ♥
ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (15, 0))

---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)

Desktop\Unicodetest.py in <module>()
      4 print('abcd kΩ ☠ °C √Hz µF ü ☃ ♥')
      5
----> 6 print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥')
      7
      8

C:\Python27\lib\encodings\cp437.pyc in encode(self, input, errors)
     10
     11     def encode(self,input,errors='strict'):
---> 12         return codecs.charmap_encode(input,errors,encoding_map)
     13
     14     def decode(self,input,errors='strict'):

UnicodeEncodeError: 'charmap' codec can't encode character u'\u2620' in position 8: character maps to <undefined>
WARNING: Failure executing file: <Unicodetest.py>

IPython внутри Spyder Python (x, y) делает то же самое, но по-разному:

In [8]: run Unicodetest.py
abcd kΩ ☠ °C √Hz µF ü ☃ ♥
------------------------------------------------------------
Traceback (most recent call last):
  File "Unicodetest.py", line 6, in <module>
    print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥')
  File "C:\Python26\lib\encodings\cp1252.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character u'\u03a9' in position 6: character maps to <undefined>

WARNING: Failure executing file: <Unicodetest.py>

sitecustomize.py , Spyder устанавливает собственную SPYDER_ENCODING на основе кодировки модуля локали, которая cp1252 для Windows 7.)

Что дает?Одна из моих команд неверна?Почему один работает на некоторых платформах, а другой работает на других платформах?Как я могу печатать символы Unicode последовательно, без сбоев или ошибок?

Есть ли альтернативный терминал для Windows, который ведет себя так же, как в Ubuntu?Кажется, что TCC-LE, Console2, Git Bash, PyCmd и т. Д. - это всего лишь обертки для cmd.exe, а не замены.Есть ли способ запустить IPython внутри интерфейса, который использует IDLE?

Ответы [ 5 ]

10 голосов
/ 19 апреля 2011

Ввод / вывод в Python (и большинстве других языков) основан на байтах . Когда вы записываете байтовую строку (str в 2.x, bytes в 3.x) в файл, байты просто записываются как есть. Когда вы записываете строку Unicode (unicode в 2.x, str в 3.x) в файл, данные должны быть закодированы в последовательность байтов.

Более подробное объяснение этого различия см. В главе Dive into Python 3 о строках .

print('abcd kΩ ☠ °C √Hz µF ü ☃ ♥')

Здесь строка является байтовой строкой. Поскольку кодировка вашего исходного файла UTF-8, байты

'abcd k\xce\xa9 \xe2\x98\xa0 \xc2\xb0C \xe2\x88\x9aHz \xc2\xb5F \xc3\xbc \xe2\x98\x83 \xe2\x99\xa5'

Оператор print записывает эти байты в консоль как есть. Но консоль Windows интерпретирует байтовые строки как закодированные в кодовой странице «OEM», которая в США равна 437 . Таким образом, строка, которую вы на самом деле видите на экране, -

abcd kΩ ☠ °C √Hz µF ü ☃ ♥

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

print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥')

При печати строки Unicode строка должна получить в кодировке в байтах. Но это работает, только если у вас есть кодировка, которая поддерживает эти символы. А ты нет.

  • В кодировке IBM437 по умолчанию отсутствуют символы ☠☃♥
  • В кодировке windows-1252 , используемой Spyder, отсутствуют символы Ω☠√☃♥.

Итак, в обоих случаях вы получаете UnicodeEncodeError, пытающуюся напечатать строку.

Что дает?

Windows и Linux использовали совершенно разные подходы к поддержке Unicode.

Первоначально они оба работали примерно одинаково: каждая локаль имеет свою собственную кодировку char на основе языка («кодовая страница ANSI» в Windows). Западные языки использовали ISO-8859-1 или windows-1252, русский язык использовал KOI8-R или windows-1251 и т. Д.

Когда Windows NT добавила поддержку Unicode (в первые дни, когда предполагалось, что Unicode будет использовать 16-битные символы), она сделала это, создав параллельную версию своего API, которая использовала wchar_t вместо char , Например, функция MessageBox была разделена на две функции:

int MessageBoxA(HWND hWnd, const char* lpText, const char* lpCaption, unsigned int uType);
int MessageBoxW(HWND hWnd, const wchar_t* lpText, const wchar_t* lpCaption, unsigned int uType);

Функции "W" являются "настоящими". Функции «A» существуют для обратной совместимости с Windows на основе DOS и в основном просто преобразуют свои строковые аргументы в UTF-16 и затем вызывают соответствующую функцию «W».

В мире Unix (в частности, Plan 9) написание совершенно новой версии POSIX API было сочтено нецелесообразным, поэтому поддержка Unicode подходила по-другому. Существующая поддержка многобайтовой кодировки в локалях CJK использовалась для реализации новой кодировки, теперь известной как UTF-8.

Предпочтение UTF-8 в Unix-подобных системах и UTF-16 в Windows - огромная проблема при написании кроссплатформенного кода, поддерживающего Unicode. Python пытается скрыть это от программиста, но печать на консоль является одной из «утечек абстракций» Джоэла.

2 голосов
/ 17 апреля 2011

Возможны две причины:

  • Кодировка Unicode с помощью print.Вы не можете вывести сырой Unicode, поэтому print необходимо выяснить, как преобразовать его в поток байтов, ожидаемый консолью (он использует sys.stdout.encoding AFAIK), что приводит нас к поддержке
  • Console.Python не контролирует ваш терминал, поэтому, если он выплевывает UTF-8, а ваш терминал ожидает чего-то другого, вы получите искаженный вывод.
0 голосов
/ 19 апреля 2011

@ dan04: Вы правы в том, что проблема в том, что кодировка файла не соответствует кодировке stdout.Тем не менее, одним из способов решения проблемы является изменение кодировки файла.Поэтому в Windows Notepad ++ можно использовать для сохранения кода с кодировкой символов UTF-8.

Альтернативой является GNU recode.

0 голосов
/ 17 апреля 2011

Вывод Unicode из Python на консоль Windows просто не работает.Python нельзя убедить испустить исходную кодировку Windows, которая ожидает широкие символы и UCS2.

0 голосов
/ 17 апреля 2011

Ваша проблема здесь в том, что ваша программа ожидает и выводит символы UTF-8, но консоли и различные исполнители Python в Интернете используют другие кодовые страницы. Нет способа кодировать специальные символы, которые работают во всех кодировках, без изменений. Однако, если вы решите использовать UTF-8 везде , вы должны быть в безопасности.

Я думаю, что подойдет любой терминал в Windows - поэтому не беспокойтесь о переключении стандартного терминала (cmd.exe) только из-за этого.Вместо этого измените кодировку терминала также на UTF-8, чтобы она соответствовала кодировке вашего скрипта Python.

К сожалению, мне никогда не удавалось найти способ установить кодовую страницу в UTF.-8 по умолчанию, поэтому это нужно делать каждый раз, когда вы открываете новую командную строку.Но это делается с помощью простой команды, так что это только наполовину плохо ... Вы меняете кодировку с помощью переключения кодовой страницы :

>chcp 65001
Current codepage is now 65001

Обратите внимание, что вы должны использовать один из стандартныхшрифты для этого работают.Большинство источников в Интернете, похоже, предлагают Lucida Console.

...