PHP и UTF-8: четыре и шесть десятых или ещё проблемные функции
С прошлого этапа я, в данном проекте, был занят тем, что писал
Более простая, но тоже не самая очевидная функция — addcslashes. Она экранирует указанные символы слешем, представляя все символы с кодом большим 128 как восьмеричное число, например, буква «ф» (в кодировке cp1251), если указать её во втором параметре, будет представлена «\364». Вопрос в следующем — символы Ответ в парной функции — stripcslashes. Если последовательно заэкранировать все байты Моя задача — эмулировать поведение функции в точности, в этой связи str_pad содержит две загадки: если задана строка дополнения из двух символов и более, то как аргумент дополняется слева и какая сторона дополняется первой, когда дополнять надо обе стороны? Как видим, сначала дополняется правая сторона, а при дополнении слева символы дополняют в указанном порядке (когда я программировал эту функцию, у меня был соблазн сделать дополнение с конца последовательности). Вообще, в PHP функций с тонкостями поведения масса. Функция wordwrap Дальше
static public function ord($character) {
if (self::ON) {
$codes = unpack('N', mb_convert_encoding($character, 'UCS-4BE', 'UTF-8'));
return reset($codes);
}
return ord($character);
}
static public function chr($ascii) {
if (self::ON) {
return mb_convert_encoding(pack('N', $ascii), 'UTF-8', 'UCS-4BE');
}
return chr($ascii);
}bolk@dev:~/daproject$ php -r 'var_dump(str_pad("..", 3, "12", STR_PAD_BOTH));'
string(3) "..1"
bolk@dev:~/daproject$ php -r 'var_dump(str_pad("..", 5, "12", STR_PAD_LEFT));'
string(3) "121.."
Комментарии 13
Сам сижу на UTF8 уже много лет, в этом году перевёл на него рабочий проект.
Моя техника перевода выглядела так.
Я искал в коде все PHP функции (в т.ч. preg_*), которые работают со строками, далее делал в IDE контекстные замены по всему проекту, часто используя регул. выражения. В некоторых случаях (их оказалось немного) делал правки в коде на месте.
На всяк. случай оставляю ссылку на мой PHP класс для обработки текста в кодировке
Немного посмотрел на ваш класс. Вот что заметил, читая по диагонали:
$char_re можно заменить на \p{L}
$cp1259_table — почти не нужна, большинство русских символов конвертируются арифметическими операциями ( http://skill.ru/artwork/1748.shtml )
метод is_utf8 может выглядеть так: return preg_match('//u', $data) ( /all/1632 )
функция ord у меня выглядит так: return reset(unpack('N', mb_convert_encoding($character,
А unpack поможет в chr.
Правда ли проверка на ascii, а потом, в случае успеха, применение операций, работающих с однобайтовыми кодировками даёт прирост скорости? Вы замеряли? Прокручивая в голове как это должно работать, мне кажется, что становится только медленнее.
Регулярные выражения у вас никак не модифицируется, тут есть проблема, я о ней писал ( /all/2704 ).
Этот кусок кода: call_user_func_array(array('self', 'str_limit'), func_get_args()) будет работать только в PHP 5.3+.
В методе trim два регулярных выражения можно объединить в одно через вертикальную черту.
Символ «~», переданный методы trim/ltrim/rtrim разрушит регулярное выражения,
str_pad работает неверно. Приращение в режимах STR_PAD_BOTH в нативной str_pad делается равномерно, в остальных режимах ваш код может добавится лишнего, если в $pad_str передать строку из нескольких символов.
Функций
Type hinting я бы:
1) заменил, где это возможно, на стандартные func(object $a, array $b), стандартные вещи понятнее остальным членам коллектива
2) остальные выкинул бы вообще, зачем думать о скорости, если по сравнению с очень медленным backtrace и reflection остальное оказывается экономией на спичках
3) а если оставил бы, то выкинул бы многочисленные преобразования strval, intval — если до них не доходит, какой толк? Кроме того, typecast много быстрее
Спасибо за замечания, Евгений.
Делая класс UTF8, я преследовал несколько целей:
Совместимость с интерфейсом стандартных PHP функций, работающих с однобайтовыми кодировками
Возможность работы без PHP расширений iconv и mbstring (если они есть, то активно используются)
Полезные функции, отсутствующие в ICONV и MBSTRING
Высокая производительность, надёжность и качественный код
PHP >= 5.1.x
Можно, но это потребует более современной библиотеки PCRE и модификатора /u, что не всегда уместно. С /u данные на валидность ещё проверяются. Это значит, PCRE будет это делать для целой строки. Если строка длинная, на это потребуется некоторое время. Возможно, PCRE имеет оптимизацию на случай, если строка не соответствует рег. выражению.
В том примере используются не все символы кодировки cp1251 (только буквы), ненадёжно!
Добавил в TODO, нужно будет сравнить мой и Ваш код по скорости
Да, я замерял.
Не очень понял, что Вы имели ввиду. Зачем это нужно в классе UTF8? Это делается 1 раз по всему проекту, рецепт для PCRE 8.10 Вы уже дали.
Спасибо, заменил на call_user_func_array(array(__CLASS__, 'str_limit'), func_get_args())
Можно, но работать будет медленнее. PCRE имеет оптимизацию, в объединённом рег. выражении она не сработает.
Учитывает,вообще-то.
Возможно, я этот метод позаимствовал и он требует дополнительного тестирования.
Методы добавляются по мере необходимости. UTF8::substr() есть!
Стандартные вещи непременно используются. К сожалению, кроме object и array PHP пока ничего не поддерживает, к тому же нельзя указать несколько возможных типов одновременно. Именно поэтому есть ReflectionTypeHint. Преимущество класса выявляется в режиме разработки кода, а в «боевом» режиме ReflectionTypeHint полностью отключается! if (! assert_options(ASSERT_ACTIVE)) return true;
пож-та, поле для ввода ответа с автоматическим изменением высоты, в зависимости от размера введённого текста. Неудобно писать большие тексты :)
P.S.
Сделайте,
Комментарий дляrin-nas.moikrug.ru:
Этот модификатор работает с PHP 4.2.3, \p работают с PHP 4.4.0/5.1.0
Валидность проверяется по ходу дела, затрат нет.
Поэтому и написано «большинство». Не читайте по диагонали.
Я не знал, что вы заменяете это по всему проекту. Не забудьте, что замены в случаях [\w] и (?:\w) делаться должныпо-разному.
Причину вы не устранили. Посмотрите справку по func_get_args
Прошу прощения, упустил.
Сначала смена дизайна, потом всё остальное :)
Да, ссылочный параметр я упустил из виду.
Как же тогда будет работать рег. выражение preg_match('//u', $data) для проверки UTF8? :)
Нельзя.
Нельзя.
Комментарий дляrin-nas.moikrug.ru:
Да, сейчас просмотрел код, действительно, вызывается функция проверки, если установлен флаг «u». Я не очень хорошо помню ваш класс, но спор начался с
и
Одна валидация наUTF-8, мне думается, должна выполняется быстрее, чем усложнённое регулярное выражение, перегруженное альтернативами.
Я не понял почему $char_re нельзя заменить на \p{L}, судя по названию (а я на него и опирался), это регулярное выражение задаёт единичный символ. Нет?
Комментарий для Евгения Степанищева:
регулярное выражение, перегруженное альтернативами.
Здесь всё зависит от рег. выражения:
Первое '~^тест~s' и второе '~^тест~su'. Первое с $char_re отработает гораздо быстрее на 100KB тексте.
\p{L} -- это только буквы. $char_re можно заменить на '.', но тогда придётся использовать флаг /u.
Комментарий дляrin-nas.moikrug.ru:
Мне это кажется очень странной оптимизацией. Понятно же, что вкаких-то случаях выигрыш даёт один подход, в каких-то — другой. Распределение случаев неизвестно.
Понятно. Смутило название переменной. Я бы назвал $any_char_re.
UTF-8), два регулярных выражения: «!(?:$char_re)!xs» и «!.!xus». Выбранное мною выражение встречается только в самом конце файла.
Я попробовал прогнать простой тест на файле в 2МБ (книга в
Оба сработали, первое ищет за 0,83 секунды, второе — за 0,14. Как видите, оптимизация излишняя.
Вот ссылка на тест: http://narod.ru/disk/27200012000/bench.zip.html
Отказался везде от $char_re и сдел его @deprecated.
Рег. выражение для буквы я бы назвал $letter_re :)