PHP и UTF-8: четыре с половиной или некоторые функции
Я пока приостановил эпопею
public static function strcmp($str1, $str2) {
// используется модуль intl
return collator_compare(collator_create(''), $str1, $str2);
}
public static function strncmp($str1, $str2, $len) {
return self::strcmp(self::substr($str1, 0, $len), self::substr($str2, 0, $len));
}
public static function strcasecmp($str1, $str2) {
return self::strcmp(self::strtolower($str1), self::strtolower($str2));
}
static public function ctype_space($text) {
return preg_match('/^\p{Zs}+$/us', $text);
}Привожу в качестве иллюстрации того, что не всё так сложно как кажется. Стоит только сесть и начать.
Некоторые функции в PHP уже есть (mb_substr),
И, кстати, в ходе родился замечательный совет: если функцию трудно реализовать (например, str_ireplace), посмотрите, может проще переписать место, где она используется. У меня две функции были использованы ровно по одному разу.
Добавлено позднее: тщательно изучил вопрос сравнения Unicode, я выяснил, что производить сравнение так, как это сделал я нельзя, поэтому функцию strcmp я удалил, буду переписывать. Например, буква «ё» в Unicode стоит дальше «л», а должно быть наоборот.
Добавлено ещё позже: в PHP 5.3 появилось расширение Intl, основанное на известной библиотеке UCI, оказывается это расширение собирается и с PHP 5.2.4+, так что имеет смысл использовать именно его.
Комментарии 13
Комментарий для Евгения Степанищева:
Нет, разумеется. Но перевод вUTF-32 эту проблему никак не решает :)
Не фиксированного. Но символы с бОльшим Unicode имеют одинаково бОльший код как вUTF-32, так и в UTF-8 (в строковом смысле).
Комментарий для SiMM:
Это бессмысленное выражение. ВUTF-32 строки ещё как-то можно побайтно сравнивать — там каждый символ занимает 4 байта. В UTF-8 русский символ займёт два байта, английский — один, а арабский — три. При сравнении побайтно такой каши из символов, буду сравниваться не символы, а их произвольные части.
Контрпример:
с точки зрения кодировки — эти буквы находятся далеко в хвосте кириллицы (коды не ниже 1194, в то время как у буквы я код 1103), а вот в алфавите они располагаются совсем иначе
http://ru.wikipedia.org/wiki/%D0%A7%D1%83% … 1.86.D1.8B
http://ru.wikipedia.org/wiki/%D0%A7%D1%83% … 0.BA.D0.B8
Вообще, если глубоко копать, можно довольно глубоко закопаться :) Вплоть до написания функций для правильной сортировки в MySQL ;)
Комментарий для Евгения Степанищева:
Ну и что?
А Вы попробуйте сравнить :) При сравнении сразу же определиться, что английский символ идёт раньше русского, и уж тем более — раньше арабского. Что вполне соответствует их кодам в Unicode :) Возможно, что приведённая в вики табличка Вам поможет понять, о чём я.
http://ru.wikipedia.org/wiki/UTF-8
Комментарий для SiMM:
Да не работает это так. Например, знак ударения — отдельный символ в Unicode, он не должен влиять на сравнение. И это только самая простая комбинация, там масса комбинирующих символов. Буква с ударением должна быть равна букве без него. Буква «ё» должна быть раньше «л» и так далее.
Сравнение Unicode выполняется вот так: http://unicode.org/reports/tr10/ Не уверен, что хочу решать эту задачу вообще.
При переводе вUTF-32 он никуда не денется. Т.е. это ничем не лучше, чем вырезать лишнее перед сравнением.
Так я к тому и клоню, что в этом случае вполне достаточно тупо байты сравнить, если уж задачу полностью не решать (если ещё и со всеми языками заморачиваться — трудоёмко, полагаю, получится).
Комментарий для SiMM:
А я и не говорил, что мой способ хорош, я говорил, что его ещё надо проверять, чем я из занимался в последнее время.
Я клонил к тому, чтобы отказать от использования strcmp и им подобных в коде, но оказалось, есть решение. См. дополнения к посту.
Хотя бы для остальных читателей :)
Ну я и не утверждал, что Вы утверждали — всего лишь привёл пример, подтверждающий, что Ваша неуверенность — не беспочвенна. Правда, про более простой пример с ё япочему-то не подумал :)