Нужны функции форматирования с поддержкой UTF8, такие как printf () и т. Д. - PullRequest
3 голосов
/ 17 февраля 2012

Я обнаружил интересную проблему при обработке строк UTF-8, содержащих символы не-ASCII, с функциями форматирования стандартной библиотеки C, такими как sprintf ():

Функции семейства printf () не знают о utf-8 и обрабатывают все, основываясь на количестве байтов, а не символов. Поэтому форматирование неверно.

Простой пример:

#include <stdio.h>

int main(int argc, char *argv[])
{
    const char* testMsg = "Tääääßt";
    char buf[1024];
    int len;

    sprintf(buf, "|%7.7s|", testMsg);
    len = strlen(buf);
    printf("Result=\"%s\", len=%d", buf, len);

    return 0;
}

Результат:

 Result="|Täää|", len=7

Скорее всего, некоторые из вас порекомендуют преобразовать приложение из char в wchar_t и использовать fwprintf () и т. Д., Но это абсолютно невозможно из-за огромных существующих приложений. Я мог бы представить написание оболочки, которая использует эти функции внутри, но это было бы сложно и очень неэффективно.

Таким образом, наилучшим решением будет замена с поддержкой UTF-8 функций форматирования стандартной библиотеки С.

В настоящее время я работаю над QNX 6.4, но отвечаю за другие операционные системы. например Linux, также очень приветствуются.

Ответы [ 2 ]

9 голосов
/ 17 февраля 2012

Ну, как только вы попросите printf выполнить интеллектуальное заполнение символов Unicode, вы столкнетесь с серьезными проблемами.Как говорится,

w͢͢͝h͡o͢͡ ̸͢k̵͟n̴͘ǫw̸̛s͘ ̀́w͘͢ḩ̵a҉̡͢t ̧̕h́o̵r͏̵rors̡ ̶͡͠lį̶e͟͟ ̶͝in͢ ͏t̕h̷̡͟e ͟͟d̛a͜r̕͡k̢̨ ͡h̴e͏r͜k̢̨ ͡h̴e͏r͜k̢̨ ͡h̴e͏a̷̢̡r̷̢̡͏ 100 100 * 100?Ну, это может быть где-то от 7 до 11, в зависимости от того, как это закодировано.Каждый ä может быть записан как U + 00E4, который является одним символом, или может быть записан как U + 0061 U + 0308, который является двумя символами.Итак, ваша следующая надежда - считать графемные кластеры.(Нет, нормализация не устранит проблему.)

Но насколько широк кластер графем?Очевидно, a имеет ширину в один столбец.U + 200B должно быть шириной ноль столбцов, это пространство "нулевой ширины".Каждый ひ ひ が な должен быть шириной в два столбца?Они обычно находятся в терминальных эмуляторах.Что происходит, когда вы форматируете な ら が な как 7 столбцов, получаете ли вы "ひらが ", который добавляет пробел, или вы получаете "ひらが", что составляет всего 6 столбцов?

Если выВырезать что-то, что смешивает текст RTL и LTR, следует ли впоследствии сбросить направление текста?Чем ты планируешь заняться?(Некоторые эмуляторы терминала, такие как Apple, поддерживают смесь текста слева направо и справа налево.)

Какова ваша цель - усекать текст?Вы пытаетесь показать пользователю строку в ограниченном пространстве, или вы пытаетесь написать формат, который использует поля фиксированной ширины?

В основном, если вы хотите вырезать текст Unicode вкуски, вы не должны делать это с чем-то простым, например printf (или wprintf, что, возможно, еще хуже)Используйте LibICU ( website ), чтобы перебрать нужные вам перерывы.Написание версии printf с поддержкой UTF-8 требует от вас всяческих неприятностей.

0 голосов
/ 06 мая 2014

Следующий фрагмент кода C99 определяет функцию u8printf, где спецификаторы формата, такие как% 10s, дают 10 кодовых точек utf-8, то есть символы, а не байты.Не забудьте установить локаль с помощью setlocale (LC_ALL, "") где-нибудь до вызова этой подпрограммы.Это работает, потому что wprintf использует wchar_t внутри.Вы можете определить u8fprintf и u8sprintf аналогичным образом.Если вы хотите записать это без массивов переменной длины C99, то также возможна подходящая комбинация malloc / free.

int u8printf(char *fmt,...){
    va_list ap;
    va_start(ap,fmt);
        int n=mbstowcs(0,fmt,0);
        if(n==-1) return -1;
        wchar_t wfmt[n+1];
        mbstowcs(wfmt,fmt,n+1);
        for(int m=128;m<=32768;m*=2){
            wchar_t wbuf[m];
            int r=vswprintf(wbuf,m,wfmt,ap);
            if(r!=-1) {
                char buf[m*4];
                wcstombs(buf,wbuf,m*4);
                fputs(buf,stdout);
                return r;
            }
        }
        return -1;
    va_end(ap);
}
...