Как конвертировать между символом и байтовой позицией в Objective-C / C / Stack Overflow

Мне нужно преобразовать позицию байта в строке UTF-8 в соответствующую позицию символа в Objective-C. Я уверен, что для этого должна быть библиотека, но я не могу ее найти — делает кто-нибудь (хотя, очевидно, любая библиотека C или C ++ сделает эту работу здесь).

Я понимаю, что могу обрезать строку UTF-8 до нужного символа, преобразовать ее в строку NSString, а затем прочитать длину строки NSString, чтобы получить ответ, но это выглядит как довольно хакерское решение проблемы, которая может быть решена вполне просто с небольшим автоматом в C.

Спасибо за вашу помощь.

2

Решение

«Характер» — это несколько двусмысленный термин, он означает что-то другое в разных контекстах. Я предполагаю, что вы хотите того же результата, что и ваш пример, [NSString length],

NSString документация не совсем заранее об этом, но [NSString length] считает количество Кодовые единицы UTF-16 в строке. Таким образом, U + 0000..U + FFFF считаются как один каждый, а U + 10000..U + 10FFFF — как два каждый. И не разбивайте суррогатные пары!

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

static const unsigned char BYTE_WIDTHS[256] = {
// 1-byte: 0xxxxxxx
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
// Trailing: 10xxxxxx
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
// 2-byte leading: 110xxxxx
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
// 3-byte leading: 1110xxxx
// 4-byte leading: 11110xxx
// invalid: 11111xxx
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0
};

size_t utf8_utf16width(const unsigned char *string, size_t len)
{
size_t i, utf16len = 0;
for (i = 0; i < len; i++)
utf16len += BYTE_WIDTHS[string[i]];
return utf16len;
}

Таблица равна 1 для 1-байтовых, 2-байтовых и 3-байтовых начальных символов UTF-8 и 2 для 4-байтовых начальных символов UTF-8, потому что при переводе на NSString,

Я сгенерировал таблицу в Haskell с помощью:

elems $ listArray (0,256) (repeat 0) //
[(n,1) | n <- ([0x00..0x7f] ++ [0xc0..0xdf] ++ [0xe0..0xef])] //
[(n,2) | n <- [0xf0..0xf7]]
1

Другие решения

Посмотрите на Кодировка UTF-8 и обратите внимание, что кодовые точки начинаются со следующих 8-битных шаблонов:

76543210 <- bit
0xxxxxxx <- ASCII chars
110xxxxx \
1110xxxx  } <- more byte(s) (of form 10xxxxxx) follow
11110xxx /

Это то, что вы должны искать при поиске начала кодовой точки.

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

Там, вероятно, даже больше к этому.

0

По вопросам рекламы [email protected]