За последние 24 часа нас посетили 19752 программиста и 1691 робот. Сейчас ищут 1722 программиста ...

Возможно ли такое организовать при помощи рег. выражений

Тема в разделе "Регулярные выражения", создана пользователем VLK, 15 июн 2014.

  1. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    В общем, всё зависит от задач...
     
  2. VLK

    VLK Старожил

    С нами с:
    15 дек 2013
    Сообщения:
    3.010
    Симпатии:
    58
    ну так я получил этот толчек :)
    за что в принципе благодарен.
    потом допилил собственный вариант отталкивая от вашего, вот он:
    Код (PHP):
    1. function grt($str) {
    2.  
    3. $fns = function($data) {
    4.     $temp = explode('|', $data[1]);
    5.     return $temp[ rand( 0, sizeof($temp)-1 ) ];
    6. };
    7.  
    8. while ( strpos($str, '{') !== false ) {
    9.     $str = preg_replace_callback('/\{([^\{]+?)\}/', $fns, $str);
    10. }          
    11.  
    12. return $str;
    13. }
    Насчет производительности я не задумывался ( особенно после того как мне отбили желание думать о ней - viewtopic.php?f=13&t=49108&p=390548 ), для меня было важно написать, коротки логический и понятный код, вроде как мне это удалось.
     
  3. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Есть определённая граница где по затратам времени от регулярок становится больше пользы чем вреда и наоборот.
    Думаю ни для кого не секрет, что php выполняет байт-код который формируется на лету.
    И если исходная строка совсем простая, то выгоднее вызвать пару обычных нативных функций чем одну регулярку. Да, в обоих случаях будет похожий байт-код с вызовом с-шной нативщины, но согласитесь, запускать грамоздкий нативный движок регулярок не совсем выгодно для простых строк. Вот и получается, что в простых случаях регулярки только жрут время процессора.
    Но если текст сложный, например если нужна поддержка {}| в тексте, то проще пройтись по тексту механизмом регулярок чем разобрать его скриптовыми алгоритмами с применением while, foreach, кучей вызовов других нативных функций и тд, т.к. всё это сгенирирует километровый байт-код на выполнение которого уйдёт куча процессорного времени(ведь на разбор и выполнение байт-кода тоже уйдёт время), вот и получается грань когда от регулярок больше пользы в плане затрат времени.

    Добавлено спустя 13 минут 48 секунд:
    В той теме вы просто попытались сделать за php работу которую он и сам знает как делать. В документации по php написано что не стоит использовать ссылки только ради оптимизации, тк php сам занимается оптимизацией таких моментов. Например один из методов внутренней оптимизации передачи аргументов является представление аргументов как жёстких ссылок в файловой системе. То есть если аргумент был изменён внутри функции куда его передали, то происходит копирование значения, если нет, то аргумент как бы сам собой передаётся по ссылке.
    Но в остальном оптимизация очень важна
     
  4. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    задачке сто лет.
    4 года назад мы уже решали её на другом форуме
     
  5. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    мои варианты

    без регулярок
    Код (PHP):
    1. $p1=$p2=false;
    2. while (($p2 = strpos($str,'}'))!==false && ($p1 = strrpos(substr($str,0,$p2),'{'))!==false) {
    3.     $a = explode('|', substr($str, $p1+1,$p2-$p1-1));
    4.     $str = substr_replace($str, $a[rand(0,count($a)-1)], $p1, $p2+1-$p1);
    5. }
    с регулярками
    Код (PHP):
    1. while (preg_match("/\{([^{}]+?)\}/", $str)) {
    2.  $str= preg_replace_callback("/\{([^{}]+?)\}/",create_function('$s','$a=explode("|",$s[1]);return $a[rand(0,count($a)-1)];'),$str);
    3. }
    время мы не мерили. интересен был сам подход.
     
  6. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Ну второй вариант тут предлагал ТС.
    Первый интересно будет замерить.
    Довольно своеобразный вариант(в хорошем смысле)
    Но что-то подсказывает что на средних и больших текстах будет медленней т.к. строка обрабатывается несколько раз(в зависимости от количества {} и каждый раз строка обрабатывается заново). И ни один вариант {}| в тексте не поддерживает.
    Ни в коем случае не в упрек, просто анализирую

    Итак, код для тестирования
    Код (PHP):
    1. //$source = '{Привет|Здравствуйте|Добрый день} Александр!';
    2. //$source = '{ей,|хей,}{привет|добрый день}';
    3. //$source = 'Привет {{Виктор|{Антон|Антонио|Антошка}|Сергей}|{Господин|Сэр|Товарищ}} как {твои|ваши} дела';
    4. //$source = 'В языке {{C++|C}|{JavaScript|PHP}|C#|Java} блоки кода можно объединять в фигурные скобки, например \{{ВАШ КОД|КАКОЙ-ТО КОД};\}<br>Условия записываются так {if(1)|if(1\|\|0)}{\{do_something();\}|\{do_some_work();\}}'; 
    5. //$source = '{{в1|в2}|{в3|{в10|в11|в12}|{в101|{в222|в333|в444}|в123}|{в13|в14|в175}}|{в6|в7}} александр {{в1|в2}|{в3|{в10|в11|в12}|{в101|{в222|в333|в444}|в123}|{в13|в14|в156}}|{в6|в7}} дмитрий';
    6. $start = microtime(true);
    7. $str = $source;
    8. $str = preg_replace_callback('#(\{[\s\S]+?\})([^\|\{\}]+)#', function($mathces)
    9. {
    10.     $mathces[1] = str_replace(array('}','{'), '', $mathces[1]);
    11.     $arr = explode('|', $mathces[1]);
    12.     return $arr[array_rand($arr)].$mathces[2]; 
    13. }, $str);
    14. echo $str.'<br>';
    15. echo number_format(microtime(true)-$start, 6).'<br><br>';
    16. //=============================================
    17. $start = microtime(true);
    18. $str = $source;
    19. $str = preg_replace_callback('#(?<!\\\)(\{[\s\S]+?(?<!\\\)\})(?![\|\}])#', function($mathces)
    20. {
    21.     $mathces[1] = preg_replace('#(?<!\\\)\{|(?<!\\\)\}#', '', $mathces[1]);
    22.     $arr = preg_split('#(?<!\\\)\|#', $mathces[1], -1,PREG_SPLIT_NO_EMPTY);
    23.     return preg_replace('#(?<!\\\)\\\#', '',$arr[array_rand($arr)]); 
    24. }, $str);
    25. echo str_replace(array('\{', '\}'), array('{', '}'), $str).'<br>'; 
    26. echo number_format(microtime(true)-$start, 6).'<br><br>';
    27. //=============================================
    28. $str = $source;
    29. $start = microtime(true);
    30. $p1=$p2=false;
    31. while (($p2 = strpos($str,'}'))!==false && ($p1 = strrpos(substr($str,0,$p2),'{'))!==false) {
    32.     $a = explode('|', substr($str, $p1+1,$p2-$p1-1));
    33.     $str = substr_replace($str, $a[rand(0,count($a)-1)], $p1, $p2+1-$p1);
    34. }
    35. echo $str.'<br>';
    36. echo number_format(microtime(true)-$start, 6);
    Результаты
    Добавлено спустя 13 минут 21 секунду:
    для строки
    ================================

    для строки
    ================================

    для строки
    ================================

    для строки
    ================================

    для строки
    Добавлено спустя 53 минуты 5 секунд:
    в вашем варианте ошибка.
    подправил
    Код (PHP):
    1. while (($p2 = strpos($str,'}'))!==false && ($p1 = strrpos(substr($str,0,$p2),'{'))!==false) 
    2. {
    3.     $sub = substr($str, $p1+1,$p2-$p1-1);
    4.     $a = explode('|', $sub);
    5.     $str = str_replace('{'.$sub.'}', $a[array_rand($a)], $str);
    6. }
    вот результаты с подправленным вариантов
    порядок тот же:
    - мой без поддержки {}| в тексте
    - мой с поддержкой {}| в тексте
    - ваш

    Для строки:
    ========================

    Для строки:
    ==============================

    Для строки:
    ===========================

    Для строки:
    ================================

    Для строки:
    обратите внимание, как я и говорил - чем больше {} тем больше времени занимает ваш вариант, т.к. рекурсия
     
  7. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    Крутяк. Темка образцовая.
     
  8. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    в вашем тоже(первом). с вложенными вариантами глючит
    например
    Код (PHP):
    1. $str = '{Пожалуйста|Просто} сделайте так, чтобы это предложение {изменялось {Быстро|Мгновенно} случайным образом}';
    иногда ваш код выдает:
    -Просто сделайте так, чтобы это предложение Мгновенно случайным образом}
    -Пожалуйста сделайте так, чтобы это предложение изменялось Быстро случайным образом}

    съедает слово 'изменялось'
     
  9. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    по условию задачи ВСЁ разделяется |

    Добавлено спустя 1 минуту 21 секунду:
    Для строки:
     
  10. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    ну я то свой код писал не под это конкретное условие. там условие было другое. потому неудивительно что мой код неработает для ВАШИХ входных данных.

    теперь ваш выдает
    Пожалуйста сделайте так, чтобы это предложение {изменялось|{Быстро|Мгновенно}|случайным образом}
    ))
     
  11. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Посмотрите предыдущие сообщения, для варианта без поддержки {}| если строка заканчивается знаком } то нужно добавить пробел в конце.
    По условиям нашей задачи всё что внутри {} будет вариантом внезависимости от уровня вложений

    Добавлено спустя 53 секунды:
    Так я же подправил его чтобы работал
     
  12. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    ненужно подменять понятия

    я привел такую сроку на вход:
    Код (PHP):
    1. $str = '{Пожалуйста|Просто} сделайте так, чтобы это предложение {изменялось {Быстро|Мгновенно} случайным образом}'; 
    Код (PHP):
    1. $str = '{Пожалуйста|Просто} сделайте так, чтобы это предложение {изменялось|Быстро|Мгновенно}|случайным образом}';
    не нужно вставлять | там где вам выгодно.
    Вторая группа скобок не обязана иметь более одного варианта. это частный случай, и он имеет право на существование. но если вам так будет легче. исправлю
    Код (PHP):
    1. $str = '{Пожалуйста|Просто} сделайте так, чтобы это {изменялось {Быстро|Мгновенно} случайным образом|не менялось}'; 
    с этим ваш код также глючит. а следовательно нет смысла мериться письками. сначало нужно чтоб алгоритм хотябы работал)
     
  13. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Ну давайте вы внимательно посмотрите и убедитесь что вы не везде поставили |
    И прочитайте пожалуйста условие задачи от ТС. вы же заявили что решили эту задачу вот мы и стали проверять ваш алгоритм.
    И никто тут письками не мериится. Если вам не хочется развиваться и узнавать что-то новое, то и не нужно в штыки всё принимать

    Если уровень вложенности влияет на вариант, то да, нужна рекурсия либо написать токенизатор чтобы не сканировать каждый раз всю строку.

    Добавлено спустя 1 минуту 2 секунды:
    Задачу ТС считаю полностью решённой ещё полторы страницы назад...
     
  14. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    я просто проверяю ваш код

    ТС написал:
    беру ваш вариант кода и приведенные ТС-ом входные данные:
    Код (PHP):
    1. $str = '{{вариант_1_1|вариант_1_2}|{вариант_2_1|вариант_2_2|вариант_2_3}}';
    ваш код выдает
    ЧЯДНТ ?
     
  15. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Ну я же вам только что написал
    Добавлено спустя 3 минуты 57 секунд:
    Либо используйте код с поддержкой {}|

    Добавлено спустя 3 минуты 30 секунд:
    По крайней мере ТС не жаловался. Если бы надо было другой формат, он бы меня поправил я думаю
     
  16. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    это называется костыль. заявленный вами код не работает на входных данных ТСа. пробел тут пробел там... тоесть на шаблоне с пробелами в разных местах ваш код неработает.
    например на таком
    Код (PHP):
    1. $str = '{{вариант_1_1|вариант_1_2} |{вариант_2_1|вариант_2_2}} ';
    извините, но код либо работает либо нет. ваш - нет. он не должен зависеть от пробела или его отсуствия.
    пока незаработает на всем спектре входных данных - даже обсуждать нечего.
     
  17. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Я что, коммерческий код тут пишу? Я итак за ТС сделал всю работу и мне тут какие-то упреки ещё...
    Если вообще всё делать за человека думать он не научится. Я думаю он в состоянии вставить в первый вариант один несчастный if на проверку последнего символа. Либо использовать второй вариант.
     
  18. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    да какие упреки. просто указал на то что есть проблемы в реализации кода.
    и нужно СНАЧАЛА решить эти проблемы.
    а уже ПОТОМ мерить его скорость и заниматься оптимизацией.

    если ТСа код устраивает - хорошо. но пусть знает где и какие проблемы могут его ждать на разных входных данных
     
  19. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Если вы всё-таки прочтете тему, то увидите что все эти моменту я для ТС расписал максимально подробно

    Добавлено спустя 1 минуту 37 секунд:
    Смысл любой помощи в том, чтобы человек что-то вынес из нее и проанализировал и возможно доработал а не тупо скопировал код

    Добавлено спустя 39 секунд:
    Иначе думать самостоятельно он не научится никогда

    Добавлено спустя 2 минуты 22 секунды:
    Вы действительно считаете что вставка пробела в конец строки и спецсимвола между неразрывными {}{} убьет производительность?
    Тем более что второй вариант лишен этих ограничений
     
  20. VLK

    VLK Старожил

    С нами с:
    15 дек 2013
    Сообщения:
    3.010
    Симпатии:
    58
    Всем участникам беседы!

    Интересуют варианты в которых будут присутствовать вложения во вложениях, т.е. вот так вот:
    Код (PHP):
    1. $str = 'Привет {{Виктор|{Антон|Антонио|Антошка}|Сергей}|{Господин|Сэр|Товарищ}} как {твои|ваши|} дела'; 
    или
    Код (PHP):
    1. $str = '{Пожалуйста|Просто} сделайте так, чтобы это предложение {изменялось|{Быстро|Мгновенно}|случайным образом}'; 
     
  21. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Просто добавьте рекурсию во второй вариант

    Добавлено спустя 3 минуты 57 секунд:
    Это если нужно учитывать уровень вложения
     
  22. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
  23. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    ваш итоговый вариант - фактически повторяет приведенный ранее мной.
    только строковые функции заменили на регулярки.
    из минусов, как вы сами говорили:
    тоесть вариант для поиска каждого блока сканирует строку ЗАНОВО сначала, КАЖДЫЙ РАЗ.

    могу предложить еще один альтернативный алгоритм (типа стековый автомат).
    плюс его в том, что он парсит все вложенные блоки ЗА ОДИН ПРОХОД:

    Код (PHP):
    1. $prev = null;
    2. $tmp = array();
    3. $i = $j =0;
    4. $out = '';
    5. $str = '{Пожалуйста|Просто} пусть \{это\} предложение {меняется {БЫСТРО|МГНОВЕННО|за {1|2|3} секунды} случайным образом|не меняется}.';
    6. preg_match_all("/./us", $str, $m);
    7. foreach($m[0] as $c) {
    8.   if ($c=='{' && $prev!='\\') { // begin block
    9.     ++$i;
    10.   } else if ($c=='|' && $prev!='\\') { // OR
    11.     ++$j;
    12.   } else if ($c=='}' && $prev!='\\') { // end block
    13.     if ($i>1) { // multi lvl 
    14.       $prev_keys = array_keys($tmp[$i-1]);
    15.       $j = array_pop($prev_keys);
    16.       // склеиваем с предыдущим значением стека
    17.       $tmp[$i-1][$j] .= $tmp[$i][array_rand($tmp[$i])];
    18.     } else { // first lvl
    19.       $out .= $tmp[$i][array_rand($tmp[$i])];
    20.       $j=0;
    21.     }
    22.     $tmp[$i] = null;
    23.     --$i;
    24.   } else { // chars
    25.     if ($i>0) {
    26.       if (!isset($tmp[$i][$j])) {
    27.         $tmp[$i][$j] = null;
    28.       }
    29.       $tmp[$i][$j] .= $c;
    30.     } else {
    31.       $out .= $c;
    32.     }
    33.   }
    34.   $prev = $c;
    35. }//foreach
    36. echo $out; 
    P.S. по скорости возможно это будет не быстрее вашего. ибо в php посимвольная работа с юникод строками не самое сильное место.
     
  24. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    Да, посимвольно будет медленно, но не только из-за юникода, но ещё и из-за количества итоговых опкодов

    Добавлено спустя 7 минут 43 секунды:
    на строке
    Код (PHP):
    1. $str = '{{Сегодня {утром|после обеда}}|Вчера} я {побежал|пошел|поехал{ на автобусе| на машине| на {трамвае|троллейбусе}|}} в {зоо-|компьютерный|интимный|продуктовый} магазин чтобы {купить|украсть} {костюм {совы|{человека паука|бэтмена}|Винни-Пуха|колобка}|презерватив}'; 
    мой - 0.0002
    ваш - 0.0004
     
  25. s.melukov

    s.melukov Новичок

    С нами с:
    31 июл 2014
    Сообщения:
    78
    Симпатии:
    0
    не совсем...
    за один проход будут заменены все блоки самого нижнего уровня сколько бы их ни было
    соответственно количество проходов = количеству уровней вложенности а не количеству блоков