Pull to refresh

Comments 57

Есть полно реализаций на других языках и несколько на PHP, но они не идеальны.

1. В вашем примере нет проверок валидности октетов и если упростить мой метод, убрав эти проверки, то он будет примерно таким-же;
2. Он всё-же на Си, а мы в блоге PHP =).

Если вас интересуют реализации на PHP, то в следующих частях я расскажу об этом подробнее и дам несколько ссылок на библиотеки.
Меня сильно смущает строка '11010000 10011111 11010001 10000000 11010000 10111000 11010000 10110010 11010000 10110101 11010001 10000010 00100000 01001000 01101001'. Зачем использовать строки с двоичным кодом?
Потому что в статье я показал как получить эту строку руками и было бы не честно писать проверочный код, который в качестве входного аргумента принимает что-то другое.

А во второй части так просто проще, например, можно руками изменить счётчик или создать другие ошибки и посмотреть, что именно с этого получится.
UFO just landed and posted this here
Чтобы вставить это строкой и не париться, в чём собственно проблема?)
UFO just landed and posted this here
Да, потеря в скорости в тестовом скрипте, который никогда не предлагается использовать в других целях, на малейшие доли секунды. И что? Вы бы ещё предложили этот метод на ассемблере реализовать…

P.S.: Нет, запятые поставить не сработает, ещё нужны скобочки везде.
UFO just landed and posted this here
Если бы вы были более внимательны, то заметили бы, что это бинарные числа и при вашем способе записи они станут десятичными.

Весь этот код только то и делает, что парсит строку, которую мы получили в статье.
UFO just landed and posted this here
UFO just landed and posted this here
По сути разницы никакой, кроме того, что нужно заниматься ручной работой, когда существует функция explode. А если строка будет хранить в таком виде пару книжек, вы тоже будете руками расставлять?)
UFO just landed and posted this here
Она изначально должна быть скопипащена со статьи, с примера по преобразованию символов в их двоичное представление на юникоде. Те у нас в любом случае входные данные — строка, и в любом случае мы будем расставлять запятые и кавычки.
Ваша функция обрабатывает некорректные последовательности по стандарту? Overlong sequences, continuation byte that is not part of a sequence, first byte that is not followed by a continuation, 5- and 6-byte sequences? Подсказываю: возвращать false — поведение не по стандарту.
Это вступительная часть, потому сейчас false — просто false. Вообще я предлагаю стрипить все невалидные последовательности.
> Вообще я предлагаю стрипить все невалидные последовательности.

То есть вы стандарт принципиально не читаете, а хотите изобрести что-то своё, ни на что не похожее, работающее неожиданным образом?
Возможно я вас просто не правильно понимаю, какого поведения ожидаете вы? Насколько помню в стандарте предлагалось выводить ошибку.
Собственно что и делается в библиотеке к части 4, я действительно не правильно вас понимал. О символе замены, кстати, написано даже в статье на англоязычной википедии, потому даже не читая стандарт это можно знать.
Что-то никак не соображу — а практический смысл этих функций какой?
Чтобы работать со строками в UTF-8 нужно иметь способ хранить их в каком-либо внутреннем состоянии, в моём случае — это массив кодов символов. Обрабатывая такой массив можно получить намного более стабильную работу с мультибайтовыми строками, нежели нативный PHP, который никуда не годится.
UFO just landed and posted this here
Ответ прост — mb_* во многих местах не работает и на ёё стабильность особо надеяться не стоит. Если хотите — посмотрите исходники, даже в оригинальных комментариях есть нотка ненависти к коду.
Об этом в 3 части будет.
> Ответ прост — mb_* во многих местах не работает и на ёё стабильность особо надеяться не стоит.
Практически везде использую — особых проблем не замечал.

> Об этом в 3 части будет.
По-моему, лучше было это во введении написать — сейчас похоже на «сделали велосипед» => «нашли для этого причину», обратная последовательность (подробно аргументированная) была бы логичнее.

Сравнение производительности вашего решения с mb_*, надеюсь, будет?
(хотя и так понятно, что оно совсем не радужное будет, но интересно)

Вашу энергию да в нужное русло… глядишь бы и в самом PHP нативная поддержка юникода бы появилась… эх, мечты.
Это ещё далеко не велосипед. Вообще, как пример, вы используете mb_detect_encoding или перегрузку методов?

Сравнение производительности будет по тем функциям, которые в mb_ вообще есть. Я не ставил цель изобрести всё заново, но исправить проблемы было бы не плохо.
Часть функционала удобнее реализовать моим методом, другую — через регулярные выражения, а третью — mbstring'ом, но чтобы не плодить состояния я решил использовать только первый вариант.

Я не знаю C на том уровне, чтобы реализовывать такие вещи. Если у вас есть лучше идеи — говорите, я напишу и реализую, но уже готовый цикл статей забрасывать не буду.
UFO just landed and posted this here
UFO just landed and posted this here
Его уже очень давно делают, много вещей в PHP 5.3 перенесли с недоработанной 6-ки. Кстати, в мануале уже появляются доки по некоторым методам.
Как краткий экскурс в UTF-8 и про него в PHP — хорошо, но как сказали выше лучше бы начали с перечисления существующих решений и того почему решили написать свое, а то правда выглядит, как велосипедостроение от незнания о существовании mbstring.func_overload = 7 )
И зачем вам калькулятор, раз у вас есть PHP?
Про PHP + UTF-8 очень хорошо написано у bolk`а в его блоге.
Я уже дописываю часть, в которой рассказывается почему «mbstring.func_overload = 7» — зло. Как минимум переопределение strlen делать нельзя, как вы потом будете работать с байтовыми потоками?
Про strlen и его вариации и их сравнения есть там же, а что перегруженная mb_ версия неправильно работает в вашем случае?
Дело в том, что перегрузка strlen в сторону mb_strlen меняет API.
Многие функции PHP требуют длинную в байтах в качестве параметра. Например, при записи в файл вам нужна длинна строки в байтах, перегрузка метода включена и strlen вернёт длинную в символах, которая меньше, чем длинна в байтах. Строка при записи будет обрезана.

Я читаю его блог, нашёл много интересных идей, спасибо за ссылку.
Ну, так и правильно )
Длина строки это длина строки, а то что вы с ее помощью меряете количество байт и является главной проблемой.
Функция правильно названа, потому что для PHP строка всегда однобайтовая, поддержки юникода то нет. Что правильного в изменении API одной с наиболее часто используемых частей языка? И как вы собираетесь делать запись не зная количество в байт?

Более того не зная правильную кодировку строки mb_strlen может возвращать неправильное значение даже в символах, а определения кодировки в mb_* нет, точнее оно не работает, совсем.
Ну, если мы работаем с UTF-8, то правильную кодировку мы знаем.
Во всех встроенных функциях чтения из файла и записи размер строки — опциональный параметр.
int fwrite ( resource $handle , string $string [, int $length ] )
int fputs ( resource $handle , string $string [, int $length ] )
string fgets ( resource $handle [, int $length ] )
tring fread ( resource $handle , int $length )

fread() reads up to length bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met:

* length bytes have been read
* EOF (end of file) is reached
* a packet becomes available or the socket timeout occurs (for network streams)
* 8192 bytes have been read (after opening userspace stream)

а для получения размера файла есть предназначенная для этого: filesize
Те. вы думаете, что ситуации, когда опциональный параметр используется — невозможны? Если это подходит под ваши задачи, то это не значит, что так можно делать всем.
Я не говорю, что это невозможно, я говорю, что в этом нет прямой необходимости, и большинстве случаев это можно убрать и кода без потерь, а там же где имеет место «посимвольное» считывание, и важна длина считываемых за итерацию данных в любом случае придется переделывать.
Просто лично мне кажется проще и прозрачнее переделать эти места и перегрузить функции, чем писать замены.
Болк доказал тестами, что mb_strlen не самый быстрый вариант, но опять таки проще переписать только его замену.
Чем меньше велосипедов, тем надежнее код и тем меньше надо подвергать его тестам.
чего только не придумают люди лишь бы не читать мануалы
mb_*
а ещё фокус preg_split('//u', 'текст', -1, PREG_SPLIT_NO_EMPTY)
Читайте пожалуйста комментарии.
u модификатор вообще пока в нерабочем состояние, только если руками собирать последнюю версию pcre и с ней собирать PHP.
может вы просто не умеете его готовить и забываете пользоваться \p{L}…
Не забываю, я просто as is его использовать не получится — нужно переписывать все регулярки, а по идее модификатор u как раз должен от этого освобождать.
мм, а обещанное продолжение будет?
Оно даже почти дописанное, но к теме как-то безынициативно отнеслись и я забил :).
хм, а я вот натолкнулся во время поисков информации об UTF8 в PHP и было интересно. Да и не так уж мало комментариев у вас
Главное не количество, а качество. По сути отписавшиеся не видят смысла в подходе с нуля и довольны работой mb_string.

P.S.: Напишите список вопросов, я объясню что ещё помню.
что такое нормализация? Что вы хотели написать про безопасность?
Смотрите. Одной с особенностей UTF-8 является то, что одинаковые на вид символы могут иметь различные представления за счёт большого количества различных модификаторов.
Например, для совместимости символ «Å» может соответствовать как значению «U+212B Å ANGSTROM SIGN», так и «U+00C5 Å LATIN CAPITAL LETTER A WITH RING ABOVE». Аналогично один символ может соответствовать нескольким числовым представлениям, например «Å» может соответствовать ещё и последовательности «U+0041 LATIN CAPITAL A U+030A COMBINING RING ABOVE». Более того, когда модификаторов несколько они могут идти в различном порядке.

Так вот, нормализация – это процесс приведения строки с одной формы в другую.

Таких форм существует 4:
• NFD (Normalization Form D: the Canonical Decomposition) – приведение к виду «элементарный символ + максимальное количество модификаторов»;
• NFKD (Normalization Form KD: the Compatibility Decomposition of the Canonical Decomposition) – аналогично, но с удалением множества способов визуально обмануть человека. Я не знаю, как доступнее это объяснить, но могу выложить картинки с наглядной демонстрацией;
• NFC (Normalization Form C: the Canonical Composition) – приведение к максимально короткому виду сохранения (это значит, что, например, все пары символ + модификатор будут заменены на символ, который внешне не будет отличаться, но память в базе будет экономить);
• NFKC (the Canonical Decomposition of the Compatibility Decomposition) – удаление множества способов визуального обмана и последующее приведение к максимально короткому виду сохранения.

Да, всё-таки стоит написать статью, текст то уже готов :).
С правильным подходом никакой опасности нет. Вообще если проводить нормализацию, то по максимуму — вам отображение страницы подпортят. Не более.

Вообще вся опасность не в самом использовании кодировки, а в работающих с ней функцией. Подобные баги очень сложно использовать, но они есть. (Фантазия сразу рисует ситуацию, где 2 неправильно обрезанные строки формируют запрещённый символ для выполнения иньекции.)
Sign up to leave a comment.

Articles