Хорошо хоть не бросать исключение. А то дойдет до абсурда PHP: <?php try { Str::Format(); } catch StringException as $e { // ... } http://code.google.com/p/strclass/sourc ... baa4bb8d0# реализовал дефолты для левых констант.
Это моя ошибка. find использует встроенные mb_strr?i?pos. Не использует встроенные stri?str метод take. И за счет изобретения велосипеда Косу удалось сделать его быстрее, чем оригинал. Кос, ты должен собой гордиться PHP: <?php require_once 'init.php'; require_once ENGINE_PATH . 'Lib/Str/IStr.php'; $test = new StrTest; /** * 0.263025045395 * 0.266486883163 * 0.252983093262 * 0.288603067398 * 0.290389060974 */ // $test->testGenerator(); /** * 3.85818910599 * 3.69979500771 * 3.74817490578 * 4.03902196884 * 3.74049687386 */ // $test->testCore(); /** * 2.79515790939 * 2.74578690529 * 2.84421300888 * 2.82366919518 * 2.76611804962 */ // $test->testWrapper(); PHP: <?php class StrTest { private $cycles = 1000; private $strLen = 1000; public function genRandStr () { $symbs = "йцУке нгШщзхї ФівАпрОл джЭячсМ итьБю"; $len = strlen($symbs); $str = ''; for($i=$this->strLen/5;$i--;) { $str .= mb_substr($symbs, mt_rand(0, $len-5), 5); } return $str; } public function testGenerator () { $s = microtime(1); for($i=$this->cycles; $i--;) { $this->genRandStr(); } echo microtime(1) - $s; } public function testCore () { $s = microtime(1); for($i=$this->cycles; $i--;) { mb_strstr ($this->genRandStr(), 'цук'); mb_stristr ($this->genRandStr(), 'іва'); mb_strstr ($this->genRandStr(), 'єяч'); mb_stristr ($this->genRandStr(), 'итьбю'); mb_strstr ($this->genRandStr(), 'ФівА'); mb_stristr ($this->genRandStr(), 'прОл'); mb_strstr ($this->genRandStr(), 'ФівА'); mb_stristr ($this->genRandStr(), 'прОл'); mb_strstr ($this->genRandStr(), 'джЭ'); mb_stristr ($this->genRandStr(), 'ить'); } echo microtime(1) - $s; } public function testWrapper () { $s = microtime(1); for($i=$this->cycles; $i--;) { Str::take('цук', $this->genRandStr(), Str::RIGHT_WITH); Str::take('іва', $this->genRandStr(), Str::RIGHT_WITH, false); Str::take('єяч', $this->genRandStr(), Str::RIGHT_WITH); Str::take('итьбю', $this->genRandStr(), Str::RIGHT_WITH, false); Str::take('ФівА', $this->genRandStr(), Str::RIGHT_WITH); Str::take('прОл', $this->genRandStr(), Str::RIGHT_WITH, false); Str::take('ФівА', $this->genRandStr(), Str::RIGHT_WITH); Str::take('прОл', $this->genRandStr(), Str::RIGHT_WITH, false); Str::take('джЭ', $this->genRandStr(), Str::RIGHT_WITH); Str::take('ить', $this->genRandStr(), Str::RIGHT_WITH, false); } echo microtime(1) - $s; } } ?>
Эта функция мало того, что глючная, так еще и с багом: Код (Text): echo Str::changeCase('t "x', Str::TITLE); T \"x
PHP: <?php $string = '...'; // echo mb_strlen($string); // 9000 function coreFunc ($string) { return mb_convert_case($string, MB_CASE_TITLE); } function symbRegexp ($string) { $pattern = '/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/ue'; return preg_replace($pattern, 'mb_strtoupper(\'$0\')', $string); } function wordRegexp ($string) { return preg_replace_callback ("/([\p{L}\p{Nd}]+)/", 'upFirst', $string); } function upFirst ($m) { return mb_strtoupper(mb_substr($m[0], 0, 1)) . mb_substr($m[0], 1); } $s = microtime(1); for ($i = 100; $i--;) { /** * 0.410108089447 * 0.414505004883 * 0.435932159424 */ // coreFunc ($string); /** * 9.61156892776 * 9.20857191086 * 9.88236117363 */ // symbRegexp ($string); /** * 1.25071811676 * 1.13894200325 * 1.21463418007 */ // wordRegexp ($string); } echo microtime(1) - $s; Вывод - в данном лучше использовать встроенную функцию, а если ее функционала не хватает - тогда уж использовать поиск слов, с которыми работать. То, что сейчас - не годится никуда. Тем более при поиске по словам более корректные результаты: PHP: <?php echo coreFunc ("привет!как дела?чем занимаешься?"), "\n"; // Привет!Как Дела?Чем Занимаешься? echo symbRegexp("привет!как дела?чем занимаешься?"), "\n"; // Привет!как Дела?чем Занимаешься? echo wordRegexp("привет!как дела?чем занимаешься?"), "\n"; // Привет!Как Дела?Чем Занимаешься?
PHP: <? function coreFunc ($string) { return mb_convert_case($string, MB_CASE_TITLE); } function symbRegexp ($string) { $pattern = '/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/ue' return preg_replace($pattern, 'mb_strtoupper(\'$0\')', $string); } резалты этих ф-ций будут разные. на входе: стрОКа стока coreFunc: Строка Стока symbRegexp: СтрОКа Стока так что сравнивать их не совсем корректно
ну предположим для ASCII $pattern = '/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/e'; можно заменить на $pattern = '/(^|\W)(\w)/e'; немного обновил, потесть сейчас http://code.google.com/p/strclass/sourc ... 9f393288b#
афаик, \w распространяется на символы, коды которых ниже 127. С кириллицей даже в win-1251 оно работать не будет, ибо она находится между 128 и 255. А вот и доказательство: Код (Text): shock@shock:~$ du -b en.php 55 en.php shock@shock:~$ cat en.php <?php var_dump(preg_match("/^[\w]+$/", "String")); ?> shock@shock:~$ php -f en.php int(1) shock@shock:~$ du -b 1251.php 55 1251.php shock@shock:~$ cat 1251.php <?php var_dump(preg_match("/^[\w]+$/", "Строка")); ?> shock@shock:~$ php -f 1251.php int(0) shock@shock:~$ du -b utf8.php 61 utf8.php shock@shock:~$ cat utf8.php <?php var_dump(preg_match("/^[\w]+$/", "Строка")); ?> shock@shock:~$ php -f utf8.php int(0) По количеству байт видно, что в файле 1251.php действительно однобайтовая кодировка, тем не менее нужные символы оно не нашло. Я не понимаю, почему ты так сопротивляешься предложенному поиску по словам, если он реально лучше по многим параметрам. kostyl, я уже ниже сказал, что функцию перепутал и быстрее встроенной функция, которая самонаписана Косом и действительно показывает лучшие результаты. Выше есть код для теста.
кстати, модификатор "u" тут не нужен: Код (Text): "/([\p{L}\p{Nd}])/ue" И еще: PHP: <?php /**/ public static function changeCase; public static function length; public static function trim; public static function reverse; /**/ private static function caseInvert; /**/ private static function caseRand; /**/ private static function charCaseInvert; /**/ private static function charCaseRand; public static function fill; Я не совсем согласен с месторасположением приватных функций. Они относятся только к методу "changeCase", а стоят между методами "reverse" и "fill". Думаю, их стоит засунуть либо в конец класса, либо в начало, либо сразу после "changeCase"
TheShock у меня вроде работает \w с кириллицей при cp1251 а находятся методы там потому что я все новые методы в конец класса добавляю. fill аоявился позже caseInvert. Да, я и сам думал перенести их.
да, мейби лучше за changeCase я запускал example/ascii.php - регистр приводит как нужно. Не через cli запускал правда. готовится к выходу скопипастенный метод size - размер строки в байтах.
у меня возникла идея улучшения одного метода. сейчас поэкспериментирую и покажу. (Юбилейный 500 пост)
Идея себя оправдала и она в следующем. При мультибайтовом поиске (метод StrU::take) мы передаем методу и заставляем его работать с мультибайтовой строкой. Сначала, при помощи self::find мы ищем позицию символа нужного вхождения (не байта, а именно символа), после этого определяем количество символов (не байт) в строке функцией mb_strlen, а потом обрезаем все функцией mb_substr. Но почему бы не работать с этими строками не как с массивом юникод-символов, а как с массивом байт? Совершенно не меняя при этом интерфейс Смотрите. Строка "метод trim" в unicode представлении - это 10 байт: 04.4C|04.35|04.42|04.3E|04.2B|?|74|72|69|6D если мы будем искать в нем по букве "м", то, конечно, можно поискать ее как символ с кодом 04.2B в многобайтовой строке, но это будет медленнее, чем искать два байта в однобайтовой строке - нету разделения на символы. Но, несмотря на способ, которым мы будем пользоваться, вернется нам один результат - строка начиная с байтов 04.2B и до конца. А теперь практика: PHP: <?php error_reporting(E_ALL | E_STRICT); mb_internal_encoding('UTF-8'); define('RIGHT', 1); define('LEFT', 2); define('RIGHT_WITH', 3); define('LEFT_WITH', 4); define('CS', true); define('CI', false); function coreTake ($what, $where, $caseSensitive = CI) { return $caseSensitive ? mb_strstr($where, $what) : mb_stristr($where, $what); } function multiTake ($what, $where, $caseSensitive = CI, $type = RIGHT_WITH) { // Поиск символов в строке $pos = ($caseSensitive) ? mb_strpos($where, $what) : mb_stripos($where, $what); if ($pos === false) return ''; switch ($type) { case RIGHT: return mb_substr($where, $pos + mb_strlen($what)); case LEFT: return mb_substr($where, 0, $pos); case RIGHT_WITH: return mb_substr($where, $pos); case LEFT_WITH: return mb_substr($where, 0, $pos + mb_strlen($what)); } } function byteTake ($what, $where, $caseSensitive = CI, $type = RIGHT_WITH) { // Поиск байт в массиве байтов $pos = ($caseSensitive) ? strpos($where, $what) : strpos(mb_strtolower($where), mb_strtolower($what)); if ($pos === false) return ''; switch ($type) { case RIGHT: return substr($where, $pos + strlen($what)); case LEFT: return substr($where, 0, $pos); case RIGHT_WITH: return substr($where, $pos); case LEFT_WITH: return substr($where, 0, $pos + strlen($what)); } } $str = str_repeat('йцУке нгШщзхї stronger bджЭячсМ итьБю ек', 13); $str.= str_repeat('холох mirrow ФівАпрОл gджЭячсМ итьБю his', 12); // echo mb_strlen($str); // 1000 - символов // echo strlen($str); // 1638 - байт $s = microtime(1); for ($i=10000; $i--;) { // Case Sensitive /** coreTake Case Sensitive * 0.299010038376 * 0.290163993835 * 0.326658964157 */ // coreTake ('олох mi', $str, CS); /** multiTake Case Sensitive * 0.271375894547 * 0.271709918976 * 0.296041965485 */ // multiTake ('олох mi', $str, CS); /** byteTake Case Sensitive * 0.108669996262 * 0.116374015808 * 0.111712932587 */ // byteTake ('олох mi', $str, CS); // Case InSensitive /** coreTake Case InSensitive * 4.53858208656 * 4.55345606804 * 4.48540306091 */ // coreTake ('олох mi', $str, CI); /** multiTake Case InSensitive * 4.34134888649 * 4.30748605728 * 4.39033198357 */ // multiTake ('олох mi', $str, CI); /** byteTake Case InSensitive * 2.72171115875 * 2.58283400536 * 2.75850009918 */ // byteTake ('олох mi', $str, CI); } echo microtime(1) - $s; ?> Ну и для того, чтобы убедиться, что все функции работают правильно: PHP: <?php echo coreTake ('r', 'string'), "\n"; // ring echo multiTake ('r', 'string'), "\n"; // ring echo byteTake ('r', 'string'), "\n"; // ring echo coreTake ('р', 'строка'), "\n"; // рока echo multiTake ('р', 'строка'), "\n"; // рока echo byteTake ('р', 'строка'), "\n"; // рока ?> UPD: Меня посетила мысль, что, возможно, byteTake может дать сбой в определенных обстоятельствах: осуществляется поиск однобайтного символа (например, D == 0x44), но первым идет двухбайтовый символ, содержащий одним из байтов такой, поиск которого осуществляется (например, ф == 0x04.0x44). Но опасения не оправдались: PHP: <?php echo byteTake ('D', 'тестофая строка зDесь'), "\n"; // Dесь ?> Для того, чтобы проверить, действительно ли соответствуют эти символы (ф и D) заявленным байтам - создайте html-документ со следующим содержимым и откройте у себя в браузере: Код (Text): ф D
о-хох. Еще утро и я не расчехлился. То есть ты утверждаешь, что mb_ ф-ции могут быть заменены ихними простыми одномайтовыми аналогами? зы: ф-ция length всегда возвращает кол-во байт? 1 символ ASCII == 1 байт. Но как быть с UNICODE? это я к тому, что чувак написал длинную ф- а ты просто тма берешь и пишешь: // echo strlen($str); // 1638 - байт mb_strtolower($what) можно будет заменить на self::changeCase и тогда весь метод take вынести в StrCommon
нет. я не утверждаю, что ВСЕ функции могут быть заменены. Но некоторые - могут. Нельзя заменить "length" , потому что она должна возвращать количество символов, а не байт, но можно заменить функции, где ищется один массив байт в другом. По крайней мере, я так думаю
Ребят, не забываем что такое setlocale и его нужно ставить, если у вас не юникодные строки! А для юникода конечно есть флаг u. А про первое все забывают и удивляются - а хрена не пашет?!
Psih я чего-то не пойму. TheShock всегда настаивает что флаг u нужно убирать. флоппик так что использовать вместо \W и \w для ASCII?
[\p{L}\p{Nd}] работает что с флагом, что без флага - одинаково. Но флаг значительно(в пять раз) замедляет работу выражения: http://www.php.ru/forum/viewtopic.php?p=163775#163775. Зачем этот флаг, если честно, я слабо понимаю ) Про локаль - да, забыл. Не работаю не с Юникодом уже очень давно. Тем более мой KWrite его не любит я вообще не осилил, что та функция делает ) имхо, она очень странная. на сколько я знаю, strlen считает именно количество байт, и пример выше это доказывает
TheShock Потому что юникодные строки из-за своей переменной длинны символов всегда медленее обычных. Это особенность UTF8 и она такова везде. Флаг нужен для таких вещей: PHP: <?php preg_match('/[а-я]+/ui', $text, $matches); Что-бы небыло торможения, надо использовать UTF-16, там все символы 2 байта, в итоге скорость работы практически такая-же.
Да, для интервалов флаг нужен, хотя и с флагом бывают пропуски. приходится дописывать чтото типа /[а-ягтіїґ]/ui про UTF-16 я тоже уже думал. мне вообще она больше 8 нравится. но все же, сейчас стандартом стала утф-8
TheShock По той простой причине, что латиница в нём всё-же 1 байт. Хотя если чисто русский текст - почему-бы не юзать UTF-16?
Psih да я знаю. он - легче по весу и он - обратно совместим со старыми кодировками и устаревшими браузерами. тем не менее, процессорное время дороже места на жестком диске, вроде