Зачем объявлять Unicode по строке в Python?

122

Я все еще изучаю питон и сомневаюсь:

В python 2.6.x я обычно объявляю кодировку в заголовке файла следующим образом (как в PEP 0263 )

# -*- coding: utf-8 -*-

После этого мои строки пишутся как обычно:

a = "A normal string without declared Unicode"

Но каждый раз, когда я вижу код проекта Python, кодировка не объявляется в заголовке. Вместо этого он объявляется в каждой строке следующим образом:

a = u"A string with declared Unicode"

Какая разница? Какая у этого цель? Я знаю, что Python 2.6.x устанавливает кодировку ASCII по умолчанию, но ее можно переопределить объявлением заголовка, так в чем смысл объявления каждой строки?

Приложение: Кажется, я перепутал кодировку файлов со строковой кодировкой. Спасибо, что объяснили :)

Оскар Карбаллал
источник
6
# coding: utf8достаточно хорошо, в этом нет необходимости-*-
медуза
1
@jellyfish, я полагаю, вы хотели напечатать # coding: utf-8.
Сэмюэл Хармер,
Должно быть #coding=utf-8. python.org/dev/peps/pep-0263
Гуантун Шен

Ответы:

167

Как отмечали другие, это две разные вещи.

Когда вы указываете# -*- coding: utf-8 -*- , вы сообщаете Python, что сохраненный вами исходный файл utf-8. По умолчанию для Python 2 используется ASCII (для Python 3 это utf-8). Это просто влияет на то, как интерпретатор читает символы в файле.

В общем, вероятно, не лучшая идея встраивать символы Юникода в ваш файл независимо от кодировки; вы можете использовать строковые escape-последовательности Unicode, которые работают в любой кодировке.


Когда вы объявляете строку с символом uвпереди , например u'This is a string', она сообщает компилятору Python, что строка является Unicode, а не байтами. Это обрабатывается интерпретатором в основном прозрачно; наиболее очевидное отличие состоит в том, что теперь вы можете вставлять символы Юникода в строку (то u'\u2665'есть теперь это разрешено). Вы можете использовать from __future__ import unicode_literalsего по умолчанию.

Это применимо только к Python 2; в Python 3 по умолчанию используется Unicode, и вам нужно указать bперед ним (например b'These are bytes', для объявления последовательности байтов).

Крис Б.
источник
Спасибо за объяснение! Я сделаю это принятым, поскольку он является наиболее полным :)
Оскар Карбаллал
2
Исходная кодировка по умолчанию для Python 2 - ascii .
Марк Толонен
27
На самом деле это отличная идея - встроить в файл высокие символы Юникода. Я сомневаюсь, что люди, не говорящие по-английски, хотят читать в своих строках escape-символы Unicode.
Марк Толонен
@Mark: Спасибо за исправление ASCII; Я быстро просмотрел PEP ( python.org/dev/peps/pep-0263 ), и он говорит о Latin-1 в преамбуле. Я не думаю, что в большинстве случаев встраивать высокие символы Юникода в файл - хорошая идея. Конечно, если вы кодируете много неанглийских строк в исходном файле, это может упростить задачу, но обычно вы делаете это для отображения пользователю, и вам, вероятно, в любом случае следует определить их в отдельном месте. А один неверно настроенный текстовый редактор может испортить все эти символы.
Крис Б.
4
согласны, если вы программируете i18nalized приложение, но подумайте, китайский вы или французский программист. Это не только строки, но и комментарии. Замечательно, что Python гибок с исходными кодировками. Python 3 может даже содержать символы, отличные от ASCII, в именах переменных.
Марк Толонен
23

Как уже говорили другие, # coding:указывает кодировку, в которой сохраняется исходный файл. Вот несколько примеров, чтобы проиллюстрировать это:

Файл сохранен на диске как cp437 (моя кодировка консоли), но не объявлена ​​кодировка

b = 'über'
u = u'über'
print b,repr(b)
print u,repr(u)

Вывод:

  File "C:\ex.py", line 1
SyntaxError: Non-ASCII character '\x81' in file C:\ex.py on line 1, but no
encoding declared; see http://www.python.org/peps/pep-0263.html for details

Вывод файла с # coding: cp437добавленными:

über '\x81ber'
über u'\xfcber'

Сначала Python не знал кодировки и жаловался на символ, отличный от ASCII. Как только он узнал кодировку, байтовая строка получила байты, которые действительно были на диске. Для строки Unicode Python читал \ x81, знал, что в cp437 это был ü , и декодировал его в кодовую точку Unicode для ü, которая является U + 00FC. Когда байтовая строка была напечатана, Python отправил шестнадцатеричное значение 81непосредственно на консоль. Когда строка Unicode была напечатана, Python правильно определил мою кодировку консоли как cp437 и преобразовал Unicode ü в значение cp437 для ü .

Вот что происходит с файлом, объявленным и сохраненным в UTF-8:

├╝ber '\xc3\xbcber'
über u'\xfcber'

В UTF-8 ü кодируется как шестнадцатеричные байты C3 BC, поэтому строка байтов содержит эти байты, но строка Unicode идентична первому примеру. Python прочитал два байта и правильно их декодировал. Python напечатал байтовую строку неправильно, потому что он отправил два байта UTF-8, представляющие ü, прямо на мою консоль cp437.

Здесь файл объявлен cp437, но сохранен в UTF-8:

├╝ber '\xc3\xbcber'
├╝ber u'\u251c\u255dber'

Строка байтов все еще C3 BCсодержит байты на диске (шестнадцатеричные байты UTF-8 ), но интерпретирует их как два символа cp437 вместо одного символа в кодировке UTF-8. Эти два символа были переведены в кодовые точки Unicode, и все печатается неправильно.

Марк Толонен
источник
10

Это не устанавливает формат строки; он устанавливает формат файла. Даже с этим заголовком "hello"это строка байтов, а не строка Unicode. Чтобы сделать его Unicode, вам придется использовать u"hello"везде. Заголовок - это всего лишь намек на то, какой формат использовать при чтении .pyфайла.

icktoofay
источник
Я тогда ошибся, подумал, что они такие же. Значит, строки Unicode используются в i18n?
Оскар Карбаллал
@Oscar: Да, по большей части. Если вы создавали веб-сайт с помощью Django или чего-то еще, и ему приходилось обрабатывать людей с символами, отличными от ASCII, то это еще одно возможное использование.
icktoofay
7

Определение заголовка должно определять кодировку самого кода, а не результирующих строк во время выполнения.

размещение символа, отличного от ascii, например ۲, в скрипте python без определения заголовка utf-8 вызовет предупреждение

ошибка

EBT
источник
-1

Я сделал следующий модуль под названием unicoder, чтобы иметь возможность выполнять преобразование переменных:

import sys
import os

def ustr(string):

    string = 'u"%s"'%string

    with open('_unicoder.py', 'w') as script:

        script.write('# -*- coding: utf-8 -*-\n')
        script.write('_ustr = %s'%string)

    import _unicoder
    value = _unicoder._ustr

    del _unicoder
    del sys.modules['_unicoder']

    os.system('del _unicoder.py')
    os.system('del _unicoder.pyc')

    return value

Тогда в своей программе вы можете сделать следующее:

# -*- coding: utf-8 -*-

from unicoder import ustr

txt = 'Hello, Unicode World'
txt = ustr(txt)

print type(txt) # <type 'unicode'>
tecnobillo
источник