strrev для UTF-8
В ходе обсуждения одной книге на «Хабре» придумал как развернуть строку
Вот что я придумал: перевожу в двухбайтную кодировку
$test = 'А роза упала на лапу Азора ウィキ';
$test = iconv('utf-8', 'utf-16le', $test);
$test = strrev($test);
// キィウ арозА упал ан алапу азор А
echo iconv('utf-16be', 'utf-8', $test);
Комментарии 56
спасибо производителям железа, не сумевшим договориться о порядке символов :D
Можно так, да. Но думаю, дольше будет.
Для читателей, такой вариант имеется ввиду:
Я так «шутил» над забаненными пользователями на форуме.
Откуда мне знать? Возможно нужно. Я, например, не знаю, что делает браузер, когда встречает dir=rtl, может переворачивает строку и печатает.
В жизни столько задач встречалось, я бы никогда не подумал, что это надо.
Гм. Но рекурсивный шаблон не умеет рекурсивно заменять же :)
Комментарий для Евгения Степанищева:
Там достаточно сложный алгоритм. Насколько я помню, он опубликовангде-то на unicode.org. Умеет корректно обрабатывать русскоязычные вставки в арабских цитатах в английских текстах, да ещё на нескольких строках.
А strrev для переворачивания строк не годится. Он даже диакритику нормально не обработает.
Годится. Для строк в однобайтовой кодировке. Его придумали в те времена, когда других и не было.
http://habrahabr.ru/post/141290/#comment_4727585
Спасибо за сравнение моего способа с конкурентами :) Но есть способ быстрее!
Я взял ваш код и попробовал ещё несколько вариантов (на своём «Маке», PHP 5.4.0):
Iter — это я взял свой итератор поUTF-8-строке ( /all/2734 ) и, итерируя им, добавлял строку с другого конца:
Соответственно, если выкинуть сам итератор и сунуть его код в цикл, должно стать ещё быстрее.
$text = «Тест перевернутой строки»;
$result = «»;
for ($index = 0; $index < strlen($text); $index ++)
{
$item = $text[$index];
$byte = ord($item);
if ($byte < 0x80)
{
$result = $item . $result;
continue;
}
if (($byte & 0xc0) == 0x80)
{
$sequence .= $item;
$count --;
if ($count == 0)
$result = $sequence . $result;
continue;
}
for ($count = 1; $count > $count) & 0xff;
$mark = (0xf80 >> $count) & 0xff;
if (($byte & $mask) == $mark)
break;
}
$sequence = $item;
}
print(«$result\n»);
Не?
Ну вы фактически то же самое сделали, что я предлагал: засунули код итератора внутрь цикла (пусть вы взяли не мой код, а аналогичный). Что я не так
Только этим кодом пользоваться неудобно, о чём я уже так же сказал. Конечно, если нужна максимальная производительность, такой вариант лучше. Но в этом случае ещё лучше написать модуль на Си (я когда работал в «Яндексе» делал модуль для быстрого измерения длины строки в
Комментарий дляcyanide-burnout.livejournal.com:
Я вас в плагиате и не обвиняю. Я говорю, что вы закодировали ту мысль, которую я написал: взять итератор и сунуть его в цикл. Что вы и проделали. Да, очевидно, что это быстрее. Я об этом и писал:
Вы в конце своего кода вопрошаете «не?». Почемуне-то, если комментарием выше я ровно то же и предлагаю сделать?
Комментарий дляcyanide-burnout.livejournal.com:
Я попробовал свою реализацию strrev, где попытался потратить свои «пять минут» на реализацию быстрого итератора:
Продолжим :)
uint8_t* sequence;
uint16_t mark;
uint16_t mask;
size_t count;
destination += strlen(source) — 1;
*destination = 0;
while (*source != 0)
{
if (*source < 0x80)
{
destination --;
*destination = *source;
source ++;
continue;
}
if ((*source & 0xc0) == 0x80)
{
sequence ++;
*sequence = *source;
source ++;
continue;
}
for (count = 2; count > count) & 0xff;
mark = (0xff00 >> count) & 0xff;
if ((*source & mask) == mark)
break;
}
if (count == 8)
return -1;
sequence = destination -= count;
*destination = *source;
source ++;
}
return 0;
char text[] = «Тест перевернутой строки»;UTF-8 format\n»);
char result[sizeof(text)];
if (strrev(text, result) == 0)
printf(«Result: %s\n», result);
else
printf(«Incorrect
Вы лучше мой код скомпилируйте в Си :)
Только чтобы такой жести не было как выше, разместите свой код на pastebin.com, а сюда ссылку скопируйте :)
Комментарий дляcyanide-burnout.livejournal.com:
Нет таких символов в стандарте.
К счастью, я мыслю на языке, на котором программирую :) Можно и на Си попробовать. Правда, я бы в этом случае лучше начал бы использовать ассемблерныеSIMD-вставки.
Смотря в каком. Может и в Unicode 6.0 нет, зто ISO/IEC 10646 предусматривает такие схемы кодирования.
Или у нас как с проблемой 2000 или с 2038 — пока петух не клюнет, все будут кодить по старому :)
А почему вы предпочитаете iconv mbstring'у?
Кстати, нельзя ли подправить ваш движок, что бы в нике вместо «<» отображался «<»?
Вернее вместо «<» «
Комментарий для hayk.livejournal.com:
Он быстрее.
Да, я уже заметил. Займусь на днях.
Комментарий для Евгения Степанищева:
А есть данныекаких-то тестов? Лет 8 назад на моих тестах mbstring был быстрее. Ну и функционал у него больше.
Спасибо.
Комментарий для hayk.livejournal.com:
Да я сегодня попробовал заменить там iconv на mb_convert_string (PHP 5.4.0), iconvчуть-чуть быстрее был.
Вот моя попытка подумать на Си:
http://pastebin.com/9wxgzqDg
Комментарий дляcyanide-burnout.livejournal.com:
Сравнение с вашим вариантом по скорости:
Комментарий для hayk.livejournal.com:
А оказывает по производительности мой первый вариант тоже всё рвёт:
http://pastebin.com/WVCFCydP
Но! Есть один кусочек там, который можно еще заоптимизировать %)
Как увидел, подумалось, можно ли еще подвылизать мой вариант…
Вот итог — http://pastebin.com/qwmNEE3D
Комментарий для Artem:
Не удалась оптимизация:
Могу вас только упрекнуть в мухляже.
Вот чистый тест на С — http://pastebin.com/7dYdQyPK
Результаты у меня такие:
С оптимизацией по O3 -
Комментарий для Artem:
Ничего не сделал. Проверьте ваш OpenID. Я именно через OpenID вам и отвечаю.
Зря. Я не мухлюю, зачем мне это? Вот запуск кода из вашего комментария:
А… Все понятно. LLVM. Используйте честеый GCC и будет вам счастье:
У меня — на GCC на Маке:
На LLVM на Маке:
На Линухах (gcc (Debian4.4.5-8) 4.4.5):
Вот теперь сравните тайминги на Маке с LLVM и GCC — все увидите. И про код тоже.