Поддержка UTF8 в кроссплатформенном приложении C - PullRequest
4 голосов
/ 21 декабря 2010

Я занимаюсь разработкой кроссплатформенного приложения C (стандарт C89), которое имеет дело с текстом UTF8. Все, что мне нужно, это базовые функции работы со строками, такие как substr, first, last и т. Д.

Вопрос 1

Существует ли библиотека UTF8, в которой реализованы вышеуказанные функции? Я уже посмотрел в отделении интенсивной терапии, и он слишком велик для моего требования. Мне просто нужно поддерживать UTF8.

Я нашел декодер UTF8 здесь . Следующие прототипы функций взяты из этого кода.

void utf8_decode_init(char p[], int length);

int utf8_decode_next();

Функция инициализации принимает массив символов, но utf8_decode_next() возвращает int. Это почему? Как можно распечатать символы, которые эта функция возвращает, используя стандартные функции, такие как printf? Функция имеет дело с символьными данными и как их можно присвоить целому числу?

Если вышеприведенный декодер не подходит для производственного кода, у вас есть рекомендации получше?

Вопрос 2

Я также запутался, читая статьи, в которых говорится, что для Unicode вам нужно использовать wchar_t. Насколько я понимаю, это не требуется, поскольку обычные строки C могут содержать значения UTF8. Я убедился в этом, посмотрев исходный код SQLite и git. SQLite имеет следующий typedef.

typedef unsigned char u8

Правильно ли мое понимание? Также, почему unsigned char требуется?

Ответы [ 6 ]

4 голосов
/ 21 декабря 2010

Вам не нужны никакие специальные библиотечные процедуры для поиска символов или подстрок с помощью UTF-8.strstr делает все, что вам нужно.В этом весь смысл UTF-8 и проектных требований, которым он был изобретен.

4 голосов
/ 21 декабря 2010
  1. Функция utf_decode_next() возвращает следующую кодовую точку Unicode.Так как Юникод является 21-битным набором символов, он не может возвращать ничего меньше int, и можно утверждать, что технически это должно быть long, поскольку int может быть 16-битной величиной.По сути, функция возвращает вам символ UTF-32.

    Чтобы распечатать широкие символы (wprintf(), <wctype.h>, <wchar.h>), вам нужно взглянуть на расширения широких символов C94 до C89.Однако одни только широкие символы не гарантированно являются UTF-8 или даже Unicode.Скорее всего, вы не можете печатать символы из utf8_decode_next() переносимо, но это зависит от ваших требований к переносимости.Чем шире диапазон систем, на которые вы должны портировать, тем меньше шансов, что все это будет работать просто.В той степени, в которой вы можете писать UTF-8 переносимым образом, вы бы отправили строку UTF-8 (а не массив символов UTF-32, полученных из utf8_decode_next()) в одну из обычных функций печати.Одной из сильных сторон UTF-8 является то, что им можно манипулировать с помощью кода, который в значительной степени его не знает.

  2. Вы должны понимать, что 4-байтовый wchar_t может содержать любойКодовая точка Unicode в одном блоке, но для UTF-8 может потребоваться от одного до четырех 8-битных байтов (1-4 единицы хранения) для хранения одной кодовой точки Unicode.Я полагаю, что в некоторых системах wchar_t может быть 16-разрядным (short) целым числом.В этом случае вы вынуждены использовать UTF-16, который кодирует кодовые точки Unicode вне базовой многоязычной плоскости (BMP, кодовые точки U + 0000 .. U + FFFF), используя две единицы хранения и суррогаты.

    Использованиеunsigned char облегчает жизнь;обычный char часто подписывается.Наличие отрицательных чисел делает жизнь труднее, чем она мне нужна (и, поверьте, это достаточно сложно, не добавляя сложности).

2 голосов
/ 21 декабря 2010

GLib имеет довольно много соответствующих функций и может использоваться независимо от GTK +.

1 голос
/ 21 декабря 2010

В Unicode более 100 000 символов. В большинстве реализаций Си имеется 256 возможных значений char.

Следовательно, UTF-8 использует более одного char для кодирования каждого символа, а декодеру требуется тип возврата, который больше char.

wchar_t - это более крупный тип, чем char (ну, у него нет , чтобы быть больше, но обычно это так). Он представляет символы заданного реализацией широкого набора символов. В некоторых реализациях (наиболее важно, в Windows, которая использует суррогатные пары для символов за пределами «базовой многоязычной плоскости»), он все еще недостаточно велик для представления любого символа Unicode, что, вероятно, является причиной, по которой используемый вами декодер использует int.

Вы не можете печатать широкие символы, используя printf, потому что он имеет дело с char. wprintf имеет дело с wchar_t, поэтому, если широкий набор символов является Unicode, и если wchar_t равен int в вашей системе (как и в Linux), тогда wprintf и друзья будут печатать вывод декодера без дальнейшая обработка. В противном случае это не так.

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

SQLite, вероятно, использовал unsigned char, так что:

  • они знают подпись - это зависит от реализации, подписано char или нет.
  • они могут выполнять сдвиги вправо и назначать значения вне диапазона, а также получать согласованные и определенные результаты для всех реализаций Си. Реализации обладают большей свободой поведения signed char, чем unsigned char.
0 голосов
/ 26 декабря 2010

Я реализовал функции substr & length, которые поддерживают символы UTF8. Этот код является модифицированной версией того, что использует SQLite.

Следующий макрос просматривает введенный текст и пропускает все многобайтовые символы последовательности. if условие проверяет, что это многобайтовая последовательность, и цикл внутри нее увеличивает input до тех пор, пока не найдет следующий главный байт.

#define SKIP_MULTI_BYTE_SEQUENCE(input) {              \
    if( (*(input++)) >= 0xc0 ) {                       \ 
    while( (*input & 0xc0) == 0x80 ){ input++; }       \
  }                                                    \
}

substr и length реализованы с использованием этого макроса.

typedef unsigned char utf8;

зиЬзЬг

void *substr(const utf8 *string, 
             int start, 
             int len, 
             utf8 **substring)
{
    int bytes, i;
    const utf8 *str2;
    utf8 *output;

    --start;
    while( *string && start ) {
        SKIP_MULTI_BYTE_SEQUENCE(string);
        --start;
    }

    for(str2 = string; *str2 && len; len--) {
        SKIP_MULTI_BYTE_SEQUENCE(str2);
    }

    bytes = (int) (str2 - string);
    output = *substring;
    for(i = 0; i < bytes; i++) {
        *output++ = *string++;
    }
    *output = '\0';
}

длина

int length(const utf8 *string)
{
    int len;
    len = 0;
    while( *string ) {
        ++len;
        SKIP_MULTI_BYTE_SEQUENCE(string);
    }
    return len;
}
0 голосов
/ 21 декабря 2010

Обычные строки C хороши для хранения данных utf8, но вы не можете легко найти подстроку в вашей строке utf8. Это связано с тем, что символ, закодированный как последовательность байтов с использованием кодировки utf8, может содержать от одного до 4 байтов в зависимости от символа. то есть «символ» не эквивалентен «байту» для utf8, как для ASCII.

Чтобы выполнить поиск по подстроке и т. Д., Вам нужно будет декодировать его в некоторый внутренний формат, который используется для представления символов Юникода, а затем выполнить поиск по подстроке. Поскольку в кодировке Unicode 256 гораздо больше символов, одного байта (или символа) недостаточно. Вот почему в найденной вами библиотеке используются целые числа.

Что касается вашего второго вопроса, возможно, это просто потому, что не имеет смысла говорить о отрицательных символах, поэтому их также можно указать как "без знака".

...