LINUX.ORG.RU

utf-8/16 библиотеки для Си

 , , ,


2

2

Пилю тихонько веб-сервис на kore.io и сейчас стал нужен шаблонизатор пока что пишу его прототип ясен пень что он тоже на Си. База будет в utf-8, кодировка html выхлопа тоже utf-8,пользовательские данные через POST тоже могут быть utf-8, пока пишу прототип не парюсь и всё обрабатывается просто как char*. Но так как никогда вплотную не работал с utf-8 боюсь граблей от переменной длинны символов. Если есть что по теме посоветовать, буду рад почитать.

Ну и по традиции cast сишников DELIRIUM, i-rinat, beastie, ncrmnt, Iron_Bug.

UDP: Довольно забавное чтиво, отражающее «всю» красоту работы с utf-8 :D https://gist.github.com/outpunk/1956399

UDP2: я ненавижу unicode

UDP3: не так страшен чёрт как его малюют utf8+char=❤

UDP4: Агрегирую всё нижесказанное и дополнительные ссылки которые могут быть полезны залётным падаванам ::)

Что-бы понять как оно в памяти лежит.

  • Перво наперво => wikipediaUTF-8

wchar_t - если хотите любой символ хранить и юзаете только системы где он 32 бита то пожалуйста, но помните даже utf-8 текст очень часто на 90% состоит из набора ASCII который занимает 1 байт и просто так увеличивать размер текста в четверо заимея лишь возможность безболезненно прыгать по нормализованным символам, ну не знаю, вам решать, можно но не нужно.

  • Код от a1batross за авторством mittorn полезно для понимания как сдвинуть символ, как перевести в другую кодировку и прочее
  • libutf Предложенный beastie предоставляет функции для насущных нужд
    typedef int32_t Rune;
    Rune *runestrcat(Rune *, const Rune *);
    Rune *runestrncat(Rune *, const Rune *, size_t);
    int runestrcmp(const Rune *, const Rune *);
    int runestrncmp(const Rune *, const Rune *, size_t);
    Rune *runestrcpy(Rune *, const Rune *);
    Rune *runestrncpy(Rune *, const Rune *, size_t);
    size_t runestrlen(const Rune *);
    Rune *runestrchr(const Rune *, Rune);
    Rune *runestrrchr(const Rune *, Rune);
    Rune *runestrdup(const Rune *);
    Rune *runestrstr(const Rune *, const Rune *);
    //и прочие не менее полезные
    
  • utf8proc от проекта языка julia написана на С и создана для жизненно необходимой нормализации utf-8.
    ** NFD normalization (@ref UTF8PROC_DECOMPOSE). */
    UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str);
    /** NFC normalization (@ref UTF8PROC_COMPOSE). */
    UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str);
    /** NFKD normalization (@ref UTF8PROC_DECOMPOSE and @ref UTF8PROC_COMPAT). */
    UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str);
    /** NFKC normalization (@ref UTF8PROC_COMPOSE and @ref UTF8PROC_COMPAT). */
    UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str);
    
  • iconv или ICU перекодировка и манипуляция с unicode и другими кодировками
  • glib utf-8/16, содержит практически всё что нужно
  • i-rinal дал очень полезную штуку в которой описаны псевдонимы кодировок, кодировка одна а названий её десяток =)
  • waker предложил свою библиотеку для манимуляций с utf-8, стоит отметить разнообразие функций и скорость.
  • Анон пнул в матчасть =) отдельное спасибо
  • mittorn поделился ссылочкой декодера и ещё одного

Ну и вроде как всё, на последок просто ссылок

https://habrahabr.ru/post/45489/ https://habrahabr.ru/post/311518/ https://ru.wikipedia.org/wiki/UTF-16 http://unicodebook.readthedocs.io/programming_languages.html http://i.voenmeh.ru/kafi5/Kam.loc/inform/UTF-8.htm https://ru.wikipedia.org/wiki/Широкий_символ http://www.cl.cam.ac.uk/~mgk25/unicode.html#c https://habrahabr.ru/post/138173/

Deleted

Ответ на: комментарий от u0atgKIRznY5

Для utf8 в подавляющем большинстве случаев подойдёт и просто char . В добавок ко всему размер wchar_t не фиксирован. Мне нужно писать железный код, и работать с utf8 так как будто это обычный char в том смысле что бы был адекватный strlen что бы я не боясь напортачить делал malloc/calloc/realloc, имел возможность получать указатель на символ как strstr и смещаться на следующий. Обрезать строку не боясь что обрежу не так и получу белиберду из за того что размеры символов разные. Принимать данные пользователей . Просто взять wchar_t вроде можно, но это хранить, я могу символ хранить и в size_t и туда влезет всё что угодно, суть в том как безопасно и прозрачно обрабатывать, пусть даже с некой абстракцией, но лучше без.

Deleted ()
Ответ на: комментарий от a1batross

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

В принципе так как шаблонизатор только для linux (другие платформы меня не интересуют) можно и правда wchar_t использовать , но я боюсь именно подводных камней

Deleted ()
Ответ на: комментарий от Deleted

Если все перечисленное и правда надо, то очевидно стоит взять либу для работы с уникодом любого вида и не пилить велосипед. Если же на самом деле ты хочешь подтереть обычные пробелы слева-справа, порезать по cr/lf и заменить везде {var} на Вася, то не надо. wchar_t не спасет в обоих случаях, т.к. ман суррогаты, ман нормализация.

anonymous ()

я использую вот эту библиотеку

она не предназначена для конвертации между utf8 и другими кодировками. для этого есть всякие iconv и ICU, которые уже вспоминали.

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

пермиссивная лицензия, брать эти файлы:

utf8.c
utf8.h
u8_lc_map.h
u8_lc_map.txt
u8_uc_map.h
u8_uc_map.txt

еще можно использовать glib, но он недостаточно портабельный, очень большой, и под LGPL.

waker ★★★★★ ()
Ответ на: комментарий от Deleted

http://www.json.org/JSON_checker/utf8_decode.c
Тут используется похожий принцип И вот небольшая программа, но она вроде пг другому работает http://nullprogram.com/blog/2017/10/06/

mittorn ★★★★★ ()
Последнее исправление: mittorn (всего исправлений: 1)
Ответ на: комментарий от sniper21

Тысяча извинений, что влез в тред

Да ладно тебе, всегда рад =)

а можете подсказать информацию по которой пишете шаблонизатор?

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

Ну к примеру берём простейший kore пример и добавляем туда шаблонизатор.

#include <kore/kore.h>
#include <kore/http.h>

int	page(struct http_request *);

int
page(struct http_request *req)
{


    template * tpl =  template_new();//инициализируем 
    template_set_tmp_dir(tpl,"/tmp/template_tmp/");//указываем где храним кэш
    //указываем где лежат другие шаблоны
    template_set_template_dir(tpl,"/home/www/awesomeproject/templates");
    //указываем какой шаблон обрабатываем сейчас
    template_set_file(tpl,"index.tpl");
    //задаём например текст title
    template_set_var(tpl,"title","account | awesomesite");
    template_set_var(tpl,"a","1");
    template_set_var(tpl,"b","2");
    //получаем GET запрос
    http_populate_get(req);

    char  *string_id;//берём из get значение
    if (http_argument_get_string(req, "id", &string_id))
    { //потом например из базы запрашиваем имя пользователя по этому id
      char * name;
      if((name = get_username_from_id(string_id)))
      {
          template_set_var(tpl,"username",name);
      }else{
          template_set_var(tpl,"username","please register and get personal id!");
      }
    };
    //запускаем template_result() который отрендерит страницу и отдаст её
    //или если страница не нуждается в обновлении отдаст то что лежит в кэше
    http_response(req, 200,template_result(),template_result_len());
    template_del(tpl);
    return (KORE_RESULT_OK);
}

теперь сам шаблон

<!doctype html>
  <head>
    <title><tpl v:title></title> #тут например v: это variable тоесть переменная
    <meta charset="utf-8">  
  </head>
  <body>
     hello, <tpl v:username>
     <tpl f:date> #тут f: это функция date() в это место будет вставлена дата
     <tpl if:v:a > v:b > #тут условие сравнения двух переменных
         <tpl i:body.tpl> #если истинно то подключаем другой шаблон, обрабатываем его и топаем дальше
     <tpl endif:>
  </body>
  </html>
Ну и всё в том же духе, если буду на что смотреть так это на пхпшный Smarty, но мне нужен очень простой, железный, быстрый как понос шаблонизатор без суперфич =)

Deleted ()
Ответ на: комментарий от sniper21

Ну и теперь когда мы «определились» как мы хотим всё что бы работало реализуем пошагово всё то что понаписали =) Естественно тут надо навешать проверки, я не указал заголовочный файл, я не указал обработчики для других шаблонов которые могут подключаться внутри основного, я не учёл что шаблоны могу быть вложенны друг в друга что вызовет рекурсивную их обработку ну например имеем index.tpl внутри него body.tpl header.tpl style.tpl footer.tpl chat.tpl news.tpl и внутри body.tpl мы подключим index.tpl (явно или по ошибке ) что вызовет рендеринг страницы внутри страницы внутри страницы внутри страницы .... =) это надо учесть, ну ещё кэш это вообще отдельная история, надо для отдельных шаблонов устанавливать флаги наверное мол этот при запросе всегда обновляется это обновляется только если запрос был позднее 30 секунд, а вот этот незачем обновлять так как он статичен, но обновлять если дата изменения самого шаблона была не совпадает с сохранённой. Ну и прочие мелочи =)

Deleted ()
Ответ на: комментарий от Deleted

Обычные статичные страницы kore и сам отдаст, он их хардкорит внутрь себя при компиляции, что не всегда удобно потому что если изменить статику то она будет доступна только после рекомпиляции и перезапуска, но это не проблема, теже 404 и тому подобные можно просто впилить напрямую. Я упомянул тут про Smarty, хоть он и пхп но всё же глубоко чхать как у него внутри кишки намотаны, главное смотреть что он может делать что бы понимать (узнавать) а что же должен делать шаблонизатор. А реализация это уже дело техники =)

Deleted ()
Ответ на: комментарий от Deleted

Тут несколько лет назад прикрепленный тред висел, очередной ежеквартальный, детально срывающий большинство покровов, но через неделю его сняли в пользу какого-то говна. Уникод и его понимание — одна из самых мутных тем в девелопменте, но логика местных модераторов бьет в этом все рекорды.

anonymous ()
Ответ на: комментарий от anonymous

Тред искал, не нашёл, но в принципе ладно, мне ещё конечно много чудных открытий предстоит узнать, но касательно utf-8 и его структуры более менее всё ясно, конечно подводных камней как я понимая тонна, то что можно в строку «Привет мир» вставить невидимых с нулевой длинной символов на гигабайт, то что есть широкая точка которую пропустят незнающие про неё резатели ссылок и возможен спам, но в целом, ненужность wchar_t понятна уже сейчас (хотя если ограничиваться linux то вполне нужен и удобен этот тип) то что utf-8 прекрасно лежит в массиве байт char, но работать с ним надо как с массивом симловов, а не байт. Да и вообще я много что почитал, но чаще одна клоунада вроде ссылки у меня в шапке этого треда (хотя и читается интересно), но по сути своей это просто тип данных с которым и работать надо соответствующим образом, обрабатывать нужно структурные единицы данных «символы», приводить к общему виду нормализация, композиция, декомпозиция. Но в отличии от той же википедии очень часто юникод и utf-8 в частности описывают как «нечто» с чем нельзя или сложно работать напрямую. Ну да ладно, читаем/пишем, всем спасибо за отзывы, за код и пояснения, тред можно закрывать.

Deleted ()

UDP: Довольно забавное чтиво, отражающее
UTF-16 чтобы штаны придержать

С одной стороны, аргумент обоснованный, т.к. муриканцы действительно не видят суслика и считают, что проблемы не существует. И повествование тоже хорошее, детальное. Хоть местами и вскользь, но толкает загуглить. С другой стороны, UTF-16 также может дать ложную уверенность, что 16-битный чар твой спаситель, и тогда покроет только часть проблем (именно поэтому многие считают wchar_t какой-то панацеей, хотя он к UTF-16 имеет такое же отношение, как нейросеть выше к логике). Плюс с 16 битами возникает endianness и богомерзкий BOM, а также несовместимость и требование перекодировки при общении в 8-битными товарищами, тысячами их. Так что вывод в гисте так себе, хотя и имеет полит.значение.

UDP2: я ненавижу unicode
UDP3: не так страшен чёрт как его малюют utf8+char=❤

Upon hearing this, the programmer was enlightened.

anonymous ()
Ответ на: комментарий от u0atgKIRznY5

Ну не бред ли?

А вы ответьте на пару простых вопросов 1. Зачем такие страсти-мордасти ради utf-8 ? 2. А зачем вообще вам эта utf-8 ? Я могу сказать свои ответы. Начну со второго. Я не вижу никаких аргументов ни в каком случае кроме совместимости с системой(США решили что эта кодировка лучше ну типа хайли лайкли У них никогда нет аргументов) за utf-8. Уже даже по этим примерам видно, что utf-8 наихудшая из всех возможных кодировок. Потому что ни в какой другой нет такой массы проблем как в этой. И все они на пустом месте... Особенно неудобна эта кодировка для русскоязычных текстов. Потому что каждый символ 2 байта, а в ASCII только один. Никакие стандартные функции посимвольного разбора для них не работают. Собственно и на первый вопрос я тоже ответил. Лучше переделать систему на cp1251 или любую ASCII кириллическую кодировку. Но вам придётся перекодировать все строки, надписи и весь хэлп иначе всё будет работать некорректно. Всё что на русском придётся переделывать. Но можно оставить на английском. Тогда не надо. Беда в том, что при взаимодействии со внешним окружением оно тоже на utf-8 вам придётся перекодировать. некоторые графические библиотеки работают исключительно с utf-8

Anatoly ()
Ответ на: комментарий от anonymous

Полюби юникод...

Потому что он намного лучше, чем utf-8. Ну хотя бы потому что там всегда известно сколько байт занимает очередной символ... Строки хоть шире, но они детерминированы. Конечно аргументов для юникода не особо много. Проблемы с символами легко решаются изменением шрифтов. Но люди почему то предпочитают не менять шрифты а решают проблему кодировкой. Это странно, но факт...

Anatoly ()
Ответ на: Re: Ну не бред ли? от anonymous

Херню не городи. В китайском языке 4-6 байтов в 1 символе, какие 2 байтные кодировки? Это только кириллица 2 байта.

6 байтов там не из-за миллиардов иероглифов, а из-за принципа кодирования, который сильно уменьшает количество битов, а следовательно и символов у более многобайтных последовательностей. Но зато улучшает восстановление синхронизации после потери/искажении байтов. А 2 байта... ну вот Микрософту хватает же.

vodz ★★★★ ()