Blog. Just Blog

Как преобразовать кириллическую строку из UTF-8 в cp1251

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Как преобразовать кириллическую строку из UTF-8 в cp1251
Как преобразовать кириллическую строку из UTF-8 в cp1251

При разработке одной программы мне понадобилось преобразовать строки на русском языке из формата UTF-8 в формат cp1251. Внезапно выяснилось, что никакие средства WinAPI не позволяются выполнить эту операцию "одной строкой". Пришлось рассматривать даже варианты с табличным преобразованием, но потом нашлось более простое решение задачи. Алгоритм преобразования получился необычный, но зато гарантированно рабочий. Может быть это поможет сохранить время и нервы кому-нибудь еще.

Для начала вспомогательная функция, которая проверяет, является ли строка кириллической строкой в формате UTF-8. В ней должны быть только однобайтовые символы из первой половины таблицы ASCII и двухбайтовые символы, соответствующие русским буквам. Нулевой символ - признак окончания строки, длина строки значения не имеет.
  1. ;--------------------------------------------------------
  2. ; Проверка строки на соответствие формату
  3. ; кириллического UTF-8
  4. ;--------------------------------------------------------
  5. ; Символы [0x00-0x7F] или двухсимвольные конструкции
  6. ; вида 0xD0[0x81|0x90-0xBF] или 0xD1[0x91|0x80-0x8F]
  7. ;--------------------------------------------------------
  8. ; На выходе:
  9. ;    EAX = 1 - строка соответствует UTF-8
  10. ;    EAX = 0 - строка не соответствует формату
  11. ;--------------------------------------------------------
  12. proc    is_utf8 tstr:DWORD
  13.         push    esi ebx
  14.         mov     esi,[tstr]
  15.  
  16.         ; По умолчанию строка соответствует формату
  17.         mov     ebx,1
  18. .loc_scan:
  19.         lodsb
  20.         ; Окончание строки?
  21.         or      al,al
  22.         jz      .loc_ret
  23.  
  24.         ; Проверка символов [0x00-0x7F]
  25.         cmp     al,07Fh
  26.         jbe     .loc_scan
  27.  
  28.         ; Проверка двухсимвольной конструкции
  29.         ; 0xD0[0x81|0x90-0xBF]
  30.         cmp     al,0D0h
  31.         jne     @f
  32.         lodsb
  33.         cmp     al,81h
  34.         je      .loc_scan
  35.         cmp     al,90h
  36.         jb      .loc_fail
  37.         cmp     al,0BFh
  38.         ja      .loc_fail
  39.         jmp     .loc_scan
  40. @@:
  41.         ; Проверка двухсимвольной конструкции
  42.         ; 0xD1[0x91|0x80-0x8F]
  43.         cmp     al,0D1h
  44.         jne     .loc_fail
  45.         lodsb
  46.         cmp     al,91h
  47.         je      .loc_scan
  48.         cmp     al,80h
  49.         jb      .loc_fail
  50.         cmp     al,8Fh
  51.         jbe     .loc_scan
  52.  
  53. .loc_fail:
  54.         ; Строка не соответствует формату
  55.         xor     ebx,ebx
  56.  
  57. .loc_ret:
  58.         mov     eax,ebx
  59.         pop     ebx esi
  60.         ret
  61. endp
Теперь пример кода для преобразования строки из UTF-8 в cp1251. Фишка заключается в том, что здесь используется промежуточный шаг с преобразованием UTF-8 в UTF-16 через функцию MultiByteToWideChar, и только после этого полученная промежуточная строка из UTF-16 преобразуется в cp1251 при помощи функции WideCharToMultiByte. Извратно, конечно, но зато работает.
  1.         ; Получить нужную длину строки для конвертирования
  2.         invoke  MultiByteToWideChar,CP_UTF8,0,str8,-1,0,0
  3.         or      eax,eax
  4.         ; Конвертировать строку невозможно
  5.         jz      can_not_convert
  6.         ; Промежуточное конвертирование UTF-8 -> UTF-16
  7.         invoke  MultiByteToWideChar,CP_UTF8,0,str8,-1,buff16,eax
  8.         ; Получить нужную длину строки для конвертирования
  9.         invoke  WideCharToMultiByte,1251,0,buff16,-1,0,0,0,0
  10.         or      eax,eax
  11.         ; Конвертировать строку невозможно
  12.         jz      can_not_convert
  13.         ; Финальное конвертирование UTF-16 -> cp1251
  14.         invoke  WideCharToMultiByte,1251,0,buff16,-1,str1251,eax,0,0
  15.  
  16. successfully_converted:
  17.         ; str1251 = отконвертированная строка
  18.         ...
  19.  
  20. can_not_convert:
  21.         ; Конвертировать строку невозможно
  22.         ...
На каждом этапе конвертирования выполняется дополнительная проверка на возможность преобразования. Если расчетная длина строки получается нулевая, значит преобразовать строку по какой-то причине нельзя. Это не отменяет использование описанной выше функции проверки на соответствие строки формату UTF-8.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

UTF8.cp1251.Demo.zip (2,256 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 7236 | Комментариев: 8

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (31.07.2016 в 17:44):
Примеры приведены. Берешь и используешь в своей программе.
Денис (31.07.2016 в 12:04):
Как это можно использовать для перекодировки?
ManHunter (03.01.2015 в 10:15):
*facepalm*
Igor K, не пиши больше ничего, не надо
Igor K (03.01.2015 в 09:24):
Перевод делается с помощью Notepad++
brute (31.10.2014 в 07:11):
morgot, я имел в виду команды SSE и другие, которые за один такт работают со строками по 128бит (или более).
morgot (29.10.2014 в 17:03):
brute, какой это "32 разрядный mov"? На 64 битах этой команды нет, по вашему? Про функцию вообще промолчу, могу сказать только то, что ваш образ мышления уж явно не Ассемблер.

ManHunter, спасибо, пригодится.
ManHunter (28.10.2014 в 10:35):
Все равно в нормальном софте придется делать обратную совместимость как минимум до WinXP
brute (28.10.2014 в 06:34):
Круто! но я бы искал какую-нибудь сишную библиотеку, чтобы заинклудить нужную функцию. Она наверняка эффективнее, чем "старый" 32 разнрядный "mov". П.С. слышал, что в W7 и W8 появилось много новых api-функций, в том числе есть и новые строковые..

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2018
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0049 сек.) / Память: 4.75 Mb
Наверх