Unicode — это очень увлекательно

Эта история произошла почти месяц назад. Постучал ко мне в скайп некий Егор.

Егор: Здравствуйте, фрилансеров ищите?)
Я: А вы что умеете?
Егор: А мы, собственно, толком ничего не умеем и хотим работать за опыт.)

Егор оказался неплохо подкованным пареньком и я предложил ему потестировать нашу либу cjCore.

Надо пояснить, что это такое. На гитхабе у нас есть репозиторий, куда мы сваливаем свои наработки, а cjCore — это одна из наших библиотек на C++.

Егор клонировал себе либу и попытался её скомпилировать, но не тут-то было. У него возникли проблемы с компиляцией нашей юникодной String.

Как известно, в стандартном С++ нет нормальных юникодных строк и поэтому многие пишут для этого свои классы. Например, в Qt написана своя QString на базе библиотеки ICU. И мы тоже решили написать свою строчку, но стали использовать не ICU, а библиотеку Boost или C++11 на выбор, кому что нравится больше.

У Егора последняя версия Ubuntu, но по какой-то причине, у него строка из C++11 компилироваться не захотела, а тянуть Boost только ради какой-то строчки он посчитал делом слишком накладным (попутно пожаловавшись на слабенький интернет) и решил использовать свободную библиотеку, в конце концов он остановился на utfcpp.sourceforge.net.

Нахрапом взять эту библиотеку Егору не удалось. Он постоянно мне скидывал возникающие ошибки, я что-то подсказывал, но постоянно что-то у него не получалось…

Тем временем я зашёл в Википедию: ru.wikipedia.org/wiki/UTF-8 и нашел там интересную табличку «Конвертирование в UTF-8»:


И тут ко мне приходит безумная мысль: написать функции преобразования UTF-8 -> UTF-32 и обратно чистым кодом, без всяких библиотек!
Я: Сейчас я кое-что скину, если у меня получится
Егор: Ок

Никаких тяжёлых операций, только проверка условий, сложение и операции с битами, 40 минут и готово!

Я: Для чего нужны либы? Чтобы быстренько своё сваял и всё работает: github.com/sitev/cjCore/blob/master/src/test_utf32to8.cpp
Егор: А обратно?
Я: Помню, сколько времени потребовалось, чтобы разобраться с кодировками в с++11 и бусте, наверное, неделя

Ещё минут 30-40 и заработала обратная конвертация.

Егор взялся за тестирование и через какое-то время скинул скриншот:


Я: Типа пашет?
Егор: Не типа, а пашет: 1 строчка — исходная, 2 строчка — UTF-32, 3 строчка — из UTF-32 в UTF-8.

Спасибо Егору, он оптимизировал получившийся код, а я его обернул в виде класса Utf: github.com/sitev/cjCore/blob/master/src/utf.cpp и попросил Егора протестировать быстродействие.

Результат не заставил себя ждать, скорость в 2-2.5 раза быстрее, чем utfcpp.sourceforge.net! К сожалению, сравнить по скорости с другими библиотеками, у нас не нашлось времени, а попробуйте сами и выложите результаты в комментариях.

Как оказалось, процесс программирования перекодировки Юникода туда-обратно очень увлекательный, заставляет с горящими глазами и учащенным сердцем быстро-быстро стучать по клавишам…
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 16

  • UFO just landed and posted this here
      0
      Что за греховная мысль…
      0
      Почему только UTF-32 <-> UTF-8? Целевая платформа — только Linux?
      • UFO just landed and posted this here
          0
          Нам для задач достаточно UTF-8 и UTF-32… Для доступа или замены i-ого символа для скорости лучше применять UTF-32, для остальных случаях, видимо UTF-8 подходит… UTF-16 что-то промежуточное и непонятно зачем нужное…
          • UFO just landed and posted this here
              +1
              UTF-16 что-то промежуточное и непонятно зачем нужное
              Ну как сказать, для Windows sizeof( wchar_t ) == 2, и в ней нативно используется UCS-2 (ранний вариант стандарта UTF-16, не допускающий последовательностей если правильно помню), в Linux sizeof( wchar_t ) == 4 и применяется UTF-32.
              Если работать с современными системами, основанными на коде WinNT, то основными вызовами там будут вызовы функций с постфиксом W, например OpenFileW(...), ожидающие на входе именно «двубайтные» символы, а если вызывать OpenFileA(...) с обычными «однобайтными», то система выполнит преобразование символов и всё равно вызовет OpenFileW(...). При этом UTF-8 не поддерживается.
              Поэтому я и спросил про целевую платформу. При использовании Windows с подходом «достаточно UTF-8 и UTF-32» возникнут огромные накладные расходы дополнительных преобразований. QChar, кстати, тоже 16-ти битный, что чревато опять же, накладными расходами, если его использовать (ведь не просто так же вы о нём в статье упоминали?).
            +6

            А с решением Björn Höhrmann не сравнивали? Его код выглядит гениально коротким и быстрым.

              0
              Нет, спасибо, надо будет сравнить
              0
              UNICODE это не только преобразование из utf-8 в utf-32 и обратно, там главное — классификация символов. В библиотеке Strutext https://github.com/merfill/strutext реализован UTF-8 итератор по байтовому потоку, а также в symbols.h определены классы символов и функция определения класса по его коду. Код итератора — это адаптация библитеки разбора utf-8 от unicode.org, там сделано хорошо и быстро. Классы символов лежат в файле UnicdeData.txt, это родная база классов символов от unicode.org, которая при сборке проекта парсится в сишные структуры.
                0
                Написать свою реализацию преобразований юникода пришло спонтанно:
                И тут ко мне приходит безумная мысль: написать функции преобразования UTF-8 -> UTF-32 и обратно чистым кодом, без всяких библиотек!
                , на изучения всего просто не хватает времени…
                0
                5 и 6-байтовых UTF-8 символов не бывает, см. RFC 3629.
                По скорости код с Qt не сравнивали?
                  0
                  С Qt надо будет обязательно сравнить, как только сделаю — отпишусь
                  0
                  Никаких тяжёлых операций, только проверка условий, ...

                  Сброс конвейера, все дела, не? Всё-таки интересно было бы увидеть сравнение с решением Björn Höhrmann (ссылка выше). Не берусь сказать, что будет быстрее.


                  Ещё можно "поиграться" со std::string::reserve(). В вашем коде оно будет вызываться явно больше одного раза (на больших строках), при желании можно сократить до одного вызова вначале.
                  Это следует учитывать и при сравнительных тестах.

                    0
                    ок, сравним
                    +2
                    Нету проверок на Overlong Sequence и прочий испорченный UTF8 — потенциальная дыра в безопасности.

                    Only users with full accounts can post comments. Log in, please.