Безопасно ли использовать `strstr` для поиска многобайтовых символов UTF-8 в строке?
После моего предыдущего вопроса: почему `strchr`, кажется, работает с многобайтовыми символами, несмотря на отказ от руководства на странице? Я понял что strchr
был плохой выбор.
Вместо этого я думаю об использовании strstr
искать один символ (многобайтовый не char
):
const char str[] = "This string contains é which is a multi-byte character";
char * pos = strstr(str, "é"); // 'é' = 0xC3A9: 2 bytes
printf("%s\n", pos);
Ouput:
é, который является многобайтовым символом
Что я и ожидаю: позиция первого байта моего многобайтового символа.
Априори, это не каноническое использование strstr
но, похоже, хорошо работает.
Этот обходной путь безопасен? Можете ли вы вспомнить какой-либо побочный эффект или особый случай, который может вызвать ошибку?
[РЕДАКТИРОВАТЬ]: я должен уточнить, что я не хочу использовать wchar_t
тип и строки, которые я обрабатываю, кодируются в кодировке UTF-8 (я знаю, что этот выбор может быть обсужден, но это неуместная дискуссия)
3 ответа
Нет strstr
не подходит для строк, содержащих многобайтовые символы.
Если вы ищете строку, которая не содержит многобайтовый символ внутри строки, которая содержит многобайтовый символ, это может дать ложное срабатывание. (При использовании кодировки shift-jis в японском языке strstr("掘 что-то", "@some") может давать ложный положительный результат)
+---------+----+----+----+
| c1 | c2 | c3 | c4 | <--- string
+---------+----+----+----+
+----+----+----+
| c5 | c2 | c3 | <--- string to search
+----+----+----+
Если конечная часть c1 (случайно) совпадает с c5, вы можете получить неверный результат. Я бы предложил использовать Unicode с функцией проверки подстрок Unicode или многобайтовыми функциями проверки подстрок. (например, _mbsstr)
редактировать
Основанный на обновленном вопросе от OP, что "может ли такой ложный положительный результат существовать в контексте UTF-8", поэтому ответ UTF-8 разработан таким образом, что он невосприимчив к частичному несовпадению символов, как показано выше, и вызывает любой ложный положительный результат, Так что это совершенно безопасно для использования strstr
с многобайтовыми символами в кодировке UTF-8.
Современные системы используют UTF-8 (или ASCII) в качестве своего многобайтового кодирования, где использование этой функции безопасно.
Чтобы строго соответствовать и заставить ваш код работать даже на старых / экзотических платформах, вам необходимо принять во внимание дополнительные проблемы.
Во-первых, хорошая новость: в каждой многобайтовой кодировке 0-байт указывает на конец строки, независимо от состояния. Это значит, ваш strstr
не вызовет сбой или что-то, но результат может быть неправильным.
В качестве примера рассмотрим UTF-7, 7-битный чистый способ кодирования Unicode. UTF-7 является многобайтовым кодированием, имеющим состояние сдвига, что означает, что интерпретация байта может зависеть от контекста, в котором он появляется. Например, (см. Википедия) "£1AKM" кодируется как +AKM-AKM
в UTF-7, где +
знак меняет состояние и толкование букв как A
, дела strstr(str, "AKM")
будет соответствовать первой части АКМ (после +
), хотя это является частью кодирования £
и на самом деле должен соответствовать AKM
часть после -
(установка состояния сдвига обратно в исходное состояние).
Этот обходной путь безопасен? Можете ли вы вспомнить какой-либо побочный эффект или особый случай, который может вызвать ошибку?
Одним из побочных эффектов является то, что если strtr()
не найдет совпадений, тогда вы напечатаете нулевое значение указателя, которое вызовет Segmentation fault
,
Вы должны проверить, имеет ли указатель значение NULL, прежде чем печатать строку. Проверьте это так:
if(pos == NULL)
printf("letter not found");
else
printf("%s\n", pos);