Отправляет email-рассылки с помощью сервиса Sendsay

Секреты программирования

  Все выпуски  

Секреты программирования - преобразование из Unicode в utf-8 и обратно


Уважаемые подписчики!

Сегодняшняя тема - преобразование из Unicode в utf-8 и обратно.
Википедия сообщает:

До сих пор наиболее распространенной кодировкой текста является ASCII (англ. American Standard Code for Information Interchange — американский стандартный код для обмена информацией; по-американски произносится [э`ски], тогда как в Великобритании чаще произносится [а`ски]; по-русски также произносится [а`ски]) — 7-битная компьютерная кодировка для представления латинского алфавита, десятичных цифр, некоторых знаков препинания, арифметических операций и управляющих символов. Вариант ASCII без национальных символов называется US-ASCII, или «International Reference Version». Впоследствии оказалось удобнее использовать 8-битные кодировки (кодовые страницы), где нижнюю половину кодовой таблицы (0—127) занимают символы US-ASCII, а верхнюю (128—255) — разные другие нужные символы.

Проблемы с перекодировкой из одной таблицы в другую, а также необходимость показывать на странице текста различные языки привели к созданию Юникод, или Уникод (англ. Unicode™) — стандарта кодирования символов, позволяющего представить знаки практически всех письменных языков. Стандарт предложен в 1991 году некоммерческой организацией «Консорциум Юникода» (англ. Unicode® Consortium, Unicode Inc.), объединяющей крупнейшие IT-корпорации. Применение этого стандарта позволяет закодировать очень большое число символов из разных письменностей: в документах Unicode могут соседствовать китайские иероглифы, математические символы, буквы греческого алфавита, латиницы и кириллицы, при этом становятся ненужными кодовые страницы. В Юникоде первые 128 символов совпадают с соответствующими символами US-ASCII.

Использование 32-битных символов казалось слишком расточительным, поэтому было решено использовать 16-битные. Таким образом, первая версия Юникода представляла собой кодировку с фиксированным размером символа в 16 бит, то есть общее число кодов было 65 536. В дальнейшем, однако, было принято решение кодировать все символы и в связи с этим значительно расширить кодовую область. Одновременно с этим, коды символов стали рассматриваться не как 16-битные значения, а как абстрактные числа.

Поскольку в ряде компьютерных систем (например, Windows NT) уже были реализованы 16-битные символы, было решено всё наиболее важное кодировать только в пределах первых 65 536 позиций (так называемая англ. Basic Multilingual Plane, BMP). Остальное пространство используется для «Дополнительных символов» (англ. Supplementary Characters): систем письма вымерших языков или очень редко используемых китайских иероглифов, математических и музыкальных символов.

Для совместимости со старыми 16-битными системами была изобретена система UTF-16, где первые 65 536 позиций отображаются непосредственно как 16-битные числа, а остальные представляются в виде «суррогатных пар» (первый элемент пары из области U+D800…U+DBFF, второй элемент пары из области U+DC00…DFFF). Поскольку в UTF-16 можно отобразить только 2**20 + 2**16 (1 114 112) символов, то это и было выбрано в качестве окончательной величины кодового пространства Юникода.

Наряду с UTF-16 был изобретен UTF-8 (от англ. Unicode Transformation Format — формат преобразования Юникода) — в настоящее время распространённая кодировка, реализующая представление Юникода, совместимое с 8-битным кодированием текста. Текст, состоящий только из символов с номером меньше 128, при записи в UTF-8 превращается в обычный текст ASCII. И наоборот, в тексте UTF-8 любой байт со значением меньше 128 изображает символ ASCII с тем же кодом. Остальные символы Юникода изображаются последовательностями длиной от 2 до 6 байтов (реально только до 4 байт, поскольку использование кодов больше 2**21 не планируется), в которых первый байт всегда имеет вид 11xxxxxx, а остальные — 10xxxxxx. Проще говоря, в формате UTF-8 символы латинского алфавита, знаки препинания и управляющие символы ASCII записываются кодами US-ASCII, a все остальные символы кодируются при помощи нескольких октетов со старшим битом 1. Это приводит к двум эффектам.

  • Даже если программа не распознаёт Юникод, то латинские буквы, арабские цифры и знаки препинания будут отображаться правильно.
  • В случае, если латинские буквы и простейшие знаки препинания (включая пробел) занимают существенный объём текста (например, в европейских языках, включая основанные на кириллице), UTF-8 даёт выигрыш по объёму по сравнению с UTF-16.

На первый взгляд может показаться, что UTF-16 удобнее, так как в ней большинство символов кодируется ровно двумя байтами. Однако это сводится на нет необходимостью поддержки суррогатных пар, о которых часто забывают при использовании UTF-16, реализовывая лишь поддержку первых 65 536 символов. Работа с UTF-8 может требовать немного больше процессорных ресурсов, так как UTF-8 является кодировкой Юникода.

Символы UTF-8 получаются из Unicode следующим образом:

Unicode двоичный UTF-8
0x00000000 — 0x0000007F 00000000 00000000 00000000 0zzzzzzz 0zzzzzzz
0x00000080 — 0x000007FF 00000000 00000000 00000yyy yyzzzzzz 110yyyyy 10zzzzzz
0x00000800 — 0x0000FFFF 00000000 00000000 xxxxyyyy yyzzzzzz 1110xxxx 10yyyyyy 10zzzzzz
0x00010000 — 0x001FFFFF 00000000 000wwwxx xxxxyyyy yyzzzzzz 11110www 10xxxxxx 10yyyyyy 10zzzzzz

Необходимость в UTF-8 объясняется еще и тем, что большинство браузеров не понимают формат UTF-16.

В качестве возможного приложения рассмотрим веб-словарь, где показывается перевод текста, например, японского на русский. Из базы данных считывается текст в формате UTF-16 и преобразуется в UTF-8. Для этого достаточно двух простых функций. Код в VBS иллюстрирует их применение. Пример на сервере

Код асп-страницы


<%@ Language=VBScript %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<%
Session.CodePage = 1251

'из Unicode в utf-8
function c2UTF8( s )
 c2UTF8 = ""
 dim k, j, ch, k1, k2, k3, m
 for k = 1 to Len( s )
  ch = Mid( s, k, 1 )
  'AscW - UTF-16 значение символа
  j = clng( AscW( ch ) )
  if j < 0 then j = 65536 + j
  if j >= 128 then
   if j < 2048 then
    '2 байта
    k1 = ( int( j / 64 ) + 128 + 64 )
    k2 = ( j mod 64 + 128 )
    c2UTF8 = c2UTF8 & Chr( k1 )
    c2UTF8 = c2UTF8 & Chr( k2 )
   else
    '3 байта
    k1 = ( int( j / (64*64) ) + 128 + 64 + 32 )
    m = j mod (64*64)
    k2 = ( int( m / 64 ) + 128 )
    k3 = ( m mod 64 + 128 )
    c2UTF8 = c2UTF8 & Chr( k1 )
    c2UTF8 = c2UTF8 & Chr( k2 )
    c2UTF8 = c2UTF8 & Chr( k3 )
   end if
  else
   c2UTF8 = c2UTF8 & ch
  end if
 next
end function

'из utf-8 в Unicode
function UTF8_16( s )
 UTF8_16 = ""
 dim i, j, j2, ch, k1, k2, k3, m
 i = 1
 do while i <= Len( s )
  ch = Mid( s, i, 1 )
  j = clng( Asc( ch ) )
  if j >= 128 then
   if j < 224 then
    '2 байта
    k1 = j mod 32
    i = i + 1
    ch = Mid( s, i, 1 )
    j2 = clng( Asc( ch ) )
    k2 = j2 mod 64
    'ChrW - символ по UTF-16 значению
    UTF8_16 = UTF8_16 & ChrW( k2 + k1 * 64 )
   else
    '3 байта
    k1 = j mod 16
    i = i + 1
    ch = Mid( s, i, 1 )
    j2 = clng( Asc( ch ) )
    k2 = j2 mod 64
    i = i + 1
    ch = Mid( s, i, 1 )
    j2 = clng( Asc( ch ) )
    k3 = j2 mod 64
    UTF8_16 = UTF8_16 & ChrW( k3 + ( k2 + k1 * 64 ) * 64 )
   end if
  else 
   UTF8_16 = UTF8_16 & ch
  end if
  i = i + 1
 loop
end function
Response.Write "Andrei" & "<br>"
Response.Write "good=" & c2UTF8("Андрей") & "<br>"
Response.Write "bad=" & "Андрей" & "<br>"
%>
</body>
</html>



Комментарии

Слово "Андрей" хранится на сервере в формате utf-16. Для правильного показа в браузере ( <meta http-equiv="Content-Type" content="text/html; charset=utf-8">) , его надо преобразовать в utf-8. Вызываем для этого функцию c2UTF8. В функциях преобразования используем AscW и ChrW - 2-х байтовые версии обычных.

Для улучшения "обратной связи" приглашаю обсудить рассылку на форуме сайта http://www.pvobr.ru в разделе "Программирование". Пишите также, какие темы вы хотите рассмотреть в будущем.

Успехов! Андрей

ЗЫ
Великому Мастеру Тьюрингу однажды приснилось, что он машина. Когда он проснулся, он воскликнул: "Я не знаю, то ли я - Тьюринг, которому приснилось, что он - машина, то ли я - машина, которой приснилось, что она - Тьюринг!"

[C] James Geoffrey

В избранное