PHP и UTF-8: четыре и шесть десятых или ещё проблемные функции

С прошлого этапа я, в данном проекте, был занят тем, что писал UTF-8-аналоги для используемых строковых функций. Всплыли некоторые проблемы, о некоторых я не думал, для других до этого момента не продумывал пути решения.

Дальше
Для начала, у нас в проекте используются ord и chr — функции получения кода символа и получения символа по его коду. Понятно, что места, в которых эти функции используются жёстко завязаны на однобайтные кодировки. В некоторых случаях ничего страшного не случится, в других эти места придётся переписать. Я нашёл несколько случаев использования, переписать пришлось пока только одно место, ещё одно я отложил на будущее. Сами функции я переписал следующим образом:

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);
    }

Более простая, но тоже не самая очевидная функция — addcslashes. Она экранирует указанные символы слешем, представляя все символы с кодом большим 128 как восьмеричное число, например, буква «ф» (в кодировке cp1251), если указать её во втором параметре, будет представлена «\364». Вопрос в следующем — символы в UTF-8 могу занимать больше одного символа, как их заэкранировать?

Ответ в парной функции — stripcslashes. Если последовательно заэкранировать все байты UTF-8-последовательности символа, то stripcslashes вернёт этот символ в первоначальное состояние. Код реализации я приводить не буду, слишком длинный. Что о нём нужно знать — последовательность экранируется только в том случае, если встречается целиком.

Моя задача — эмулировать поведение функции в точности, в этой связи str_pad содержит две загадки: если задана строка дополнения из двух символов и более, то как аргумент дополняется слева и какая сторона дополняется первой, когда дополнять надо обе стороны?

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.."

Как видим, сначала дополняется правая сторона, а при дополнении слева символы дополняют в указанном порядке (когда я программировал эту функцию, у меня был соблазн сделать дополнение с конца последовательности).

Вообще, в PHP функций с тонкостями поведения масса. Функция wordwrap (по-умолчанию) преобразовывает длинную строку в абзац заданной ширины, разрывая строку так, чтобы не разорвать слова. А вот какими символами, по мнению этой функции, разделяются слова? Мои эксперименты показывают, что это пробел и «\n». Интересно, что «\r\n» эта функция не разрывает! Так же нужно не забыть про то, что пробелы, вообще говоря, сохраняются, заменяется на символ переноса только необходимый минимум.

13 комментариев

«Community»

Только что, в шестнадцатой серии сериала «Сообщество», Абед Надир произнёс, напиваясь, «будем здоровы» по-русски (!).

Отличный сериал, кстати. Спасибо всем тем, кто советовал.

16 комментариев