суббота, 22 декабря 2012 г.

Кодирование строк Unicode в Python


Экранированные значения байтов в шестнадцатеричном виде:

\xNN

например:
\xc4
\x84

Экранированные значения символов Unicode из четырех шестнадцатеричных цифр
( 2- байтовые 16 - битные)  коды символов:

\uNNNN

например:
\u00C4

Экранированные значения символов Unicode из восьми шестнадцатеричных цифр
( 4- байтовые 32 - битные)  коды символов:

\UNNNNNNNN

например:
\U000000E8

 Шестнадцатеричное представление ASCII - кодов символов:

O - 4F
R - 52
A - 41
C - 43
L - 4C
E - 45


>>> s = '\x4f\x52\x41\x43\x4c\x45'
>>> s
'ORACLE'
>>>

Символы UNICODE  2-х  байтовые (16- битные):

O - 004F
R - 0052
A - 0041
C - 0043
L - 004C
E - 0045

>>> s = '\u004f\u0052\u0041\u0043\u004c\u0045'
>>> s
'ORACLE'
>>>

Символы UNICODE  4-x  байтовые (32 - битные):

 >>> s = '\U0000004f\U00000052\U00000041\U00000043\U0000004c\U00000045'
>>> s
'ORACLE'
>>>


Кодирование - процесс преобразования строки символов в последовательность простых байтов в соответствии с желаемой кодировкой.

Декодирование - процесс преобразования последовательности байтов в строку символов в соответствии с желаемой кодировкой.


Пример кодирования:

Преобразуем объект типа str (последовательность символов unicode) в объект типа bytes (последовательность байтов, т.е. коротких целых чисел)

>>> s = 'Java'
>>> s.encode()
b'Java'
>>>

Фактически объекты типа bytes, возвращаемые данной операцией кодирования строки символов, в действительности являются последовательностью коротких целых чисел, которые просто выводятся как символы ASCII, когда это возможно.

Так как мы не указали желаемой кодировки, то наши символы были преобразованы в последовательность простых байтов в соответствии с кодировкой по умолчанию:

>>> import sys
>>> sys.platform
'win32'
>>>
>>> sys.getdefaultencoding()
'utf-8'
>>>

А вообще функцию str.encode() следует вызывать явно указывая кодировку:

s.encode('utf-8')


Пример декодирования:

Существует и обратная функция, которая преобразует последовательность простых байтов в строку и на основе объекта типа bytes создает объект типа str.

>>> b = b'Java'
>>> b.decode()
'Java'
>>>

Тут также, если кодировка не указана, то используется по умолчанию.


Существуют еще две функции кодирования и декодирования:

Кодирование:
Функция    bytes(s, encoding)

Декодирование:
Функция    str(b, encoding)

в этих функциях параметр encoding является обязательным.

Первая функция вообще выдаст ошибку, если не указать параметр encoding:

>>> s = 'Java'

>>> bytes(s)
Traceback (most recent call last):
  File "", line 1, in
    bytes(s)
TypeError: string argument without an encoding
>>>


А вторая сработает, но вернет вместо объекта str  строковую форму объекта bytes (это не то, что требуется):

>>> b = b'Java'
>>> str(b)
"b'Java'"
>>>

Используйте эти две функции всегда с параметром encoding:

>>> s = 'Java'
>>> bytes(s, encoding='ascii')
b'Java'
>>>


>>> b = b'Java'
>>> str(b, encoding='ascii')
'Java'
>>>


Еще примеры:

>>> s = 'ORACLE'
>>> s.encode('ascii')
b'ORACLE'
>>>

>>> s.encode('latin-1')
b'ORACLE'
>>>

>>> s.encode('utf-8')
b'ORACLE'
>>>

Объекты типа bytes, в действительности являются последовательностью коротких целых чисел, которые выводятся как символы  ASCII, когда это возможно:

>>> s = 'ORACLE'
>>> s.encode('latin-1')[0]
79
>>>

>>> list(s.encode('latin-1'))
[79, 82, 65, 67, 76, 69]
>>>


Рассмотрим еще несколько примеров:

Кодировка по умолчанию у нас utf-8

Рассмотрим строковый символ 'O'

Его ASCII представление : '\x4f'
Его unicode представление : '\u004f'   или  '\U0000004f'

Данный символ в кодировке по умолчанию (utf-8) будет иметь код в виде байта с целочисленным значением:

>>> ord('O')
79
>>> ord('\x4f')
79
>>> ord('\u004f')
79
>>> ord('\U0000004f')
79
>>>

Как видим, неважно в каком представлении мы передали строковый символ функции ord().
Его код в кодировке utf-8 имеет одно значение.

При декодировании данного байта кода, используя кодировку по умолчанию (utf-8) мы получим представление символа которому соответствует этот код:

>>> chr(79)
'O'
>>>


Еще примеры:

Возьмем любой символ ASCII

>>> s = 'G'
>>>

Его байтовое представление находим так:

>>> s = 'G'

>>> ord(s)
71

>>> hex(71)
'0x47'
>>>

Значит его можно представить:

>>> s = '\x47'
>>> s
'G'
>>> s = '\u0047'
>>> s
'G'
>>> s = '\U00000047'
>>> s
'G'
>>>
>>> s.encode('utf-8')
b'G'
>>>
>>> b = b'G'
>>>
>>> list(b)
[71]
>>>
>>> b.decode('utf-8')
'G'
>>>


Возьмем не ASCII символ:

>>> s = 'Ä'
>>>


Его байтовое представление находим так:

>>> ord(s)
196
>>> hex(196)
'0xc4'
>>>


Значит его можно представить:

>>> s = '\xc4'
>>> s
'Ä'
>>>
>>> s = '\u00c4'
>>> s
'Ä'
>>>
>>> s = '\U000000c4'
>>> s
'Ä'
>>>
>>> s.encode('utf-8')
b'\xc3\x84'
>>>
>>> b = b'\xc3\x84'
>>>
>>> list(b)
[195, 132]
>>>
>>> b.decode('utf-8')
'Ä'
>>>


Возьмем символ  '茶'  (этот символ в китае  означает слово чай)

>>> s = '茶'
>>>
>>> ord(s)
33590
>>> hex(33590)
'0x8336'
>>>

В однобайтовом виде его уже не представить

>>> s = '\u8336'
>>> s
'茶'
>>>
>>> s = '\U00008336'
>>> s
'茶'
>>>
>>> s.encode('utf-8')
b'\xe8\x8c\xb6'
>>>
>>> b = b'\xe8\x8c\xb6'
>>>
>>> list(b)
[232, 140, 182]
>>>
>>> b.decode('utf-8')
'茶'
>>>

Любая строка хранится в памяти компьютера в виде последовательности символов, однако эти символы могут представляться различными способами, в зависимости от того, какой набор символов используется.

Набор символов ASCII - это символы с кодами в диапазоне  от 0 до 127.
(Что позволяет сохранять каждый символ в одном 8-битовом байте, в котором фактически используется только 7 младших байтов)

Некоторые стандарты позволяют использовать все возможные значения 8-битных байтов от 0 до 255, чтобы обеспечить возможность представления специальных символов, отображая их в диапазоне значений от 128 до 255 (за пределами диапазона ASCII).

Один из таких стандартов, известный под названием Latin-1, широко  используется в западной европе.

В некоторых алфавитах так много символов, что нет никакой возможности представить каждый из них одним байтом.

Стандарт Unicode обеспечивает более гибкие возможности.
Каждый символ в строке Unicode может быть представлен несколькими байтами.

Чтобы хранить текст строки Unicode в памяти компьютера, его необходимо транслировать в последовательность простых байтов и обратно, используя определенную кодировку.

Для некоторых кодировок процесс преобразования тривиально прост:

в  кодировках ASCII и Latin-1, например, каждому символу соответствует единственный байт, поэтому фактически никакого преобразования не требуется.

Для других кодировок процедура отображения может оказаться намного сложнее и порождать по несколько байтов для каждого символа.

Кодировка UTF-8 позволяет представить широкий диапазон символов, используя схему с переменным числом байтов.

Символы с кодами в диапазоне от 128 до 2047  преобразуются в двухбайтовые последовательности, где каждый байт имеет значение от 128 до 255.

Символы с кодами выше 2047 преобразуются в трех или четырехбайтовые последовательности, где каждый байт имеет значение от 128 до 255.

Строки с символами ASCII остаются компактными.
Набор ASCII является подмножеством обеих кодировок, Latin-1 и UTF-8.

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


С точки зрения программиста на языке Python, кодировки определяются как строки, содержащие названия кодировок.

Язык Python поддерживает примерно 100 различных кодировок.
Полный список можно посмотреть так:

>>> import encodings

>>> help(encodings)

...........................................
...........................................

PACKAGE CONTENTS
    aliases
    ascii
    base64_codec
    big5
    big5hkscs
    bz2_codec
    charmap
    cp037
    cp1006
    cp1026
    cp1140
    cp1250
    cp1251
    cp1252
    cp1253
    cp1254
    cp1255
    cp1256
    cp1257
    cp1258
    cp424
    cp437
    cp500
    cp720
    cp737
    cp775
    cp850
    cp852
    cp855
    cp856
    cp857
    cp858
    cp860
    cp861
    cp862
    cp863
    cp864
    cp865
    cp866
    cp869
    cp874
    cp875
    cp932
    cp949
    cp950
    euc_jis_2004
    euc_jisx0213
    euc_jp
    euc_kr
    gb18030
    gb2312
    gbk
    hex_codec
    hp_roman8
    hz
    idna
    iso2022_jp
    iso2022_jp_1
    iso2022_jp_2
    iso2022_jp_2004
    iso2022_jp_3
    iso2022_jp_ext
    iso2022_kr
    iso8859_1
    iso8859_10
    iso8859_11
    iso8859_13
    iso8859_14
    iso8859_15
    iso8859_16
    iso8859_2
    iso8859_3
    iso8859_4
    iso8859_5
    iso8859_6
    iso8859_7
    iso8859_8
    iso8859_9
    johab
    koi8_r
    koi8_u
    latin_1
    mac_arabic
    mac_centeuro
    mac_croatian
    mac_cyrillic
    mac_farsi
    mac_greek
    mac_iceland
    mac_latin2
    mac_roman
    mac_romanian
    mac_turkish
    mbcs
    palmos
    ptcp154
    punycode
    quopri_codec
    raw_unicode_escape
    rot_13
    shift_jis
    shift_jis_2004
    shift_jisx0213
    tis_620
    undefined
    unicode_escape
    unicode_internal
    utf_16
    utf_16_be
    utf_16_le
    utf_32
    utf_32_be
    utf_32_le
    utf_7
    utf_8
    utf_8_sig
    uu_codec
    zlib_codec


Строки в Python


Строка - последовательность символов Unicode

Символ - строка, имеющая длину 1


"Строка символов"
'Еще одна строка символов !!!'
''  # Пустая строка

Доступ к элементам строки

>>> "Строка символов"
'Строка символов'

>>> "Строка символов"[5]
'а'

>>> "Строка символов"[8]
'и'

>>> "Строка символов"[0]
'С'
>>>

Индексы в Python начинаются с нуля
Символы можно только извлекать, но не изменять их.


Строку, содержащую только символы целых чисел, можно преобразовать в целое число.

>>> int("73475690")
73475690
>>>

причем начальные и конечные пробелы допускаются

>>> int("   73475690 ")
73475690
>>>

Проверим, что тип действительно int

>>> print (int("   73475690 "), type(int("   73475690 ")))
73475690 < class 'int '>
>>>

Преобразовать число в строку можно так:

>>> str(26540978)
'26540978'
>>>

Проверим, что тип действительно str

>>> print (str(26540978), type(str(26540978)))
26540978 < class 'str' >
>>>

Можно создать объект типа str в памяти и затем создать ссылку на на этот объект.
Например создадим объект типа str с текстом "python" и затем создадим на этот объект ссылку с именем a.

>>> a = "python"
>>> a
'python'
>>>

оператор "=" связывает ссылку на объект с объектом находящимся в памяти

Если ссылка на объект уже существует, ее легко можно связать с другим объектом:

>>> a = "jython"
>>> a
'jython'
>>>

Теперь ссылка с именем a ссылается на объект типа str с текстом "jython"

А на строку  "python" теперь ни одна ссылка не ссылается
и интерпретатор сможет утилизировать объект содержащий эту строку "сборщиком мусора".

Еще пример:

>>> a = "python"
>>> b = "jython"
>>> c = a
>>> print (a, b, c)
python jython python
>>> c = b
>>> print (a, b, c)
python jython jython
>>> a = c
>>> print (a, b, c)
jython jython jython
>>>

Теперь ни одна ссылка не ссылается на строку "python",
интерпретатор сможет утилизировать объект содержащий эту строку.

Как мы уже видели, в интерактивной оболочке достаточно просто ввести имя ссылки на объект,
чтобы интерпретатор вывел значение связанного с ней объекта.

>>> a, b, c
('jython', 'jython', 'jython')
>>>

Python вывел значения в круглых скобках, разделив их запятыми
Так обозначается тип данных tuple (кортеж), т.е. упорядоченная,
неизменяемая последовательность объектов.
Однако, чтобы вывести значение объекта по связанной с ним ссылке из программы или модуля
следует использовать функцию print()


В Python используется динамический контроль типов, т.е. ссылки на объекты в любой момент могут
повторно привязываться к различным объектам
(которые могут относиться к данным различных типов)

Имена ссылок - это идентификаторы
Они
- не могут совпадать с ключевыми словами.
- должны начинаться с алфавитного символа или символа подчеркивания,
  за которым следует ноль или более алфавитных символов, символов подчеркивания или цифр.
Ограничений на длину не накладывается.
Идентификаторы чувствительны к регистру.


Строки в Python - это неизменяемый тип данных str, который хранит последоватедьность символов Unicode.

Строковые объекты создаются :

- при вызове функций str() или bytes()
- при манипуляции объектом типа файл созданным вызовом функции open()
- при определении строкового литерала (в одинарных, двойных или тройных кавычках)

Для создания строковых объектов можно использовать функцию  str(),
которая без аргументов возвращает пустую строку:

>>> str()
''
>>> 


С аргументом, который является строкой - возвращается его копия :

>>> str("Oracle Java!")
'Oracle Java!'
>>>


С аргументом, который не является строкой - возвращается строковое представление аргумента :

>>> str(3.1415)
'3.1415'
>>> 


Функция str() может также использоваться как функция преобразования.
В этом случае первый аргумент должен быть объектом, который можно преобразовать в строку (например типа bytes  или bytearray)  и кроме того функции может быть передано до двух необязятельных строковых аргументов:

- один из них определяет используемую кодировку
- второй определяет порядок обработки ошибок кодирования

>>> s="Häuser Höfe Bücher"

>>> bytes(s, encoding='latin-1')
b'H\xe4user H\xf6fe B\xfccher'
 

>>> 'H\xe4user H\xf6fe B\xfccher'
'Häuser Höfe Bücher'
 

>>> str(b'H\xe4user H\xf6fe B\xfccher')
"b'H\\xe4user H\\xf6fe B\\xfccher'"
 

>>> str(b'H\xe4user H\xf6fe B\xfccher', "latin-1")
'Häuser Höfe Bücher'
 

>>> str(b'H\xe4user H\xf6fe B\xfccher', "ascii")
Traceback (most recent call last):
  File "", line 1, in
    str(b'H\xe4user H\xf6fe B\xfccher', "ascii")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 1: ordinal not in range(128)
 

>>> str(b'H\xe4user H\xf6fe B\xfccher', "ascii", "ignore")
'Huser Hfe Bcher'
 

>>> str(b'H\xe4user H\xf6fe B\xfccher', "ascii", "replace")
'H�user H�fe B�cher'
>>>


Литералы строк создаются с использованием кавычек или апострофов, при использовании важно, чтобы с обоих концов литерала использовались кавычки одного и того же типа.

Можно использовать строки в тройных кавычках либо в тройных апострофах.
Когда используется такая форма, все строки в программном коде объединяются в одну строку, а там, где в исходном тексте выполняется переход на новую строку, вставляется символ "конец строки".  Это весьма полезно для оформления в сценариях Python крупных блоков текста:

>>> msg = """ строка текста
еще строка текста в "кавычках" и 'апострофах'
продолжение на следующей строке """
>>>
>>> msg
' строка текста\nеще строка текста в "кавычках" и \'апострофах\'\nпродолжение на следующей строке '
>>>


>>> a = "Здесь 'апострофы' можно не экранировать, а "кавычки" нужно экранировать."
SyntaxError: invalid syntax

>>> a = "Здесь 'апострофы' можно не экранировать, а \"кавычки\" нужно экранировать."

>>> a
'Здесь \'апострофы\' можно не экранировать, а "кавычки" нужно экранировать.'
>>>


>>> a = 'Здесь 'апострофы' придется экранировать, а "кавычки" можно не экранировать.'
SyntaxError: invalid syntax
 

>>> a = 'Здесь \'апострофы\' придется экранировать, а "кавычки" можно не экранировать.'
 

>>> a
'Здесь \'апострофы\' придется экранировать, а "кавычки" можно не экранировать.'
>>>



В языке Python символ перевода строки интерпретируется как завершающий символ инструкции, но
- не внутри круглых скобок  ( )
- не внутри квадратных скобок  [ ]
- не внутри фигурных скобок  { }
- не внутри тройных кавычек  """   """

В тройных кавычках символ перевода строки можно даже экранировать :

>>> s = """ aaaaaaaaaaa\
bbbbbbbbbbbbbbbbbbbbbbb
ccccccccccccc """
>>> s
' aaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbb\nccccccccccccc '
>>>


Первый перевод строки экранирован, в итоге вместо трех мы получили две строки.

Мы можем вллючать символы перевода строки в любые строковые литералы с помощью экранированной последовательности \n.

 >>> s = "Строка\nНовая строка"
 

>>> s
'Строка\nНовая строка'
 

>>> print(s)
Строка
Новая строка
>>>



В языке Python допустимы следующие экранированные последовательности:


\перевод_строки    - экранирует(т.е. игнорирует) символ перевода строки

\\    - экранирует символ обратного слэша

\'     - экранирует символ апострофа

\"     - экранирует символ кавычки

\a     - символ ascii сигнал (bell, BEL)

\b     - символ ascii  забой (backspace, BS)

\f     - символ ascii  перевод формата (formfeed, FF)

\n     - символ ascii  перевод строки (linefeed, LF)

\N{название}     - символ Unicode с заданным названием

\000     - символ с заданным восьмеричным кодом (от \000 до \377)

\r     - символ ascii возврат каретки (carriage return, CR)

\t     - символ ascii табуляция (tab, TAB)

\v     - символ ascii  вертикальная табуляция (vertical tab, VT)

\uhhhh     - символ Unicode с указанным 16-битным шестнадцатеричным значением.

\Uhhhhhhhh     - символ Unicode с указанным 32-битным шестнадцатеричным значением.

\xhh     -  символ c  указанным 8-битным шестнадцатеричным значением.



В некоторых ситуациях, например,  при записи регулярных выражений, приходится создавать строки с большим количеством символов обратного слэша.
Это может вызывать определенные неудобства, т.к. каждый такой символ придется экранировать:



>>> import re
>>> phone1 = re.compile("^((?:[(]\\d+[)])?\\s*\\d+(?:-\\d+)&)$")
>>>

Решить эту проблему можно используя "сырые" (raw) строки.
Это обычные строки в кавычках или тройных кавычках, в которые перед первой кавычкой добавлен символ r.

Внутри таких строк все символы интерпретируются как обычные символы, поэтому отпадает необходимость экранировать символы, которые в других типах строк имеют специальное значение.

>>> phone2 = re.compile(r"^((?:[(]\d+[)])?\s*\d+(?:-\d+)&)$")
>>>

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

>>> m = "это длинный строковый литерал, который занимает" +\
    " вторую строку" +\
    " и даже третью"
>>> m
'это длинный строковый литерал, который занимает вторую строку и даже третью'
>>>

Но более приятный способ с использованием круглых скобок :

>>> m = ("это длинный строковый литерал, который занимает"
     " вторую строку"
     " и даже третью")
>>> m
'это длинный строковый литерал, который занимает вторую строку и даже третью'
>>>