Ввод / вывод в 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 пытается скрыть это от программиста, но печать на консоль является одной из «утечек абстракций» Джоэла.