Intereting Posts
RegEx исключает академическое название Сброс соединения SSL Laravel 4 и способ развертывания приложения с использованием FTP php require_once не работает так, как я хочу. Относительный путь Проверяйте дубликаты перед вставкой Поисковое ключевое слово (-ы), которое соответствует данным в MySQL Как я могу поместить двойные кавычки внутри строки в ответ ajax JSON от php? Допустимо ли распространять функции библиотеки PHP исключительно для изменения имен? Проблема SED (linux) с рекурсивным как загрузить изображение на сервере с помощью ajax в кордовой сборке WooCommerce – Сделать набор купонов, добавив фиксированную плату к заказу Как получить во время выполнения название маршрута в Symfony2 при использовании описания маршрутов yaml? PHP session_id никогда не меняется при обновлении Как удалить повторяющиеся значения из многомерного массива в PHP Как STDIN и STDOUT с PHP и Python использовать html2text и получить отформатированный текст?

Заменить повторяющиеся строки в строке

Я пытаюсь найти (и заменить) повторяющуюся строку в строке.

Моя строка может выглядеть так:

Lorem ipsum dolor sit amet sit amet sit amet sit nostrud Упражнение amit sit ullamco labis nisi ut aliquip ex ea commodo, следовательно.

Это должно стать:

Lorem ipsum dolor sit amet sit nostrud exercise amit sit ullamco labis nisi ut aliquip ex ea commodo, следовательно.

Обратите внимание, что amit sit не удаляется, поскольку он не повторяется.

Или строка может быть такой:

Lorem ipsum dolor sit amet () sit amet () sit amet () sit nostrud Упражнение ullamco labis nisi ut aliquip aliquip ex ea commodo ensat.

которые должны стать:

Lorem ipsum dolor sit amet () sit nostrud exercise ullamco labis nisi ut aliquip ex ea commodo, следовательно.

Таким образом, это не просто az, но также может иметь другие (ascii) символы. Я очень рад, если кто-то поможет мне в этом.

Следующим шагом было бы сопоставить (и заменить) что-то вроде этого:

2 вопроса 3 вопроса 4 вопроса 5 вопросов

который станет следующим:

2 вопроса

Число в конечном выходе может быть любым числом 2,3,4, это не имеет значения. В последнем примере будут только разные числа, но слова будут одинаковыми.

    Код решения первой задачи:

     <?php function split_repeating($string) { $words = explode(' ', $string); $words_count = count($words); $need_remove = array(); for ($i = 0; $i < $words_count; $i++) { $need_remove[$i] = false; } // Here I iterate through the number of words that will be repeated and check all the possible positions reps for ($i = round($words_count / 2); $i >= 1; $i--) { for ($j = 0; $j < ($words_count - $i); $j++) { $need_remove_item = !$need_remove[$j]; for ($k = $j; $k < ($j + $i); $k++) { if ($words[$k] != $words[$k + $i]) { $need_remove_item = false; break; } } if ($need_remove_item) { for ($k = $j; $k < ($j + $i); $k++) { $need_remove[$k] = true; } } } } $result_string = ''; for ($i = 0; $i < $words_count; $i++) { if (!$need_remove[$i]) { $result_string .= ' ' . $words[$i]; } } return trim($result_string); } $string = 'Lorem ipsum dolor sit amet sit amet sit amet sit nostrud exercitation amit sit ullamco laboris nisi ut aliquip ex ea commodo consequat.'; echo $string . '<br>'; echo split_repeating($string) . '<br>'; echo 'Lorem ipsum dolor sit amet sit nostrud exercitation amit sit ullamco laboris nisi ut aliquip ex ea commodo consequat.' . '<br>' . '<br>'; $string = 'Lorem ipsum dolor sit amet () sit amet () sit amet () sit nostrud exercitation ullamco laboris nisi ut aliquip aliquip ex ea commodo consequat.'; echo $string . '<br>'; echo split_repeating($string) . '<br>'; echo 'Lorem ipsum dolor sit amet () sit nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'; ?> 

    Второй код решения задачи:

     <?php function split_repeating($string) { $words = explode(' ', $string); $words_count = count($words); $need_remove = array(); for ($i = 0; $i < $words_count; $i++) { $need_remove[$i] = false; } for ($j = 0; $j < ($words_count - 1); $j++) { $need_remove_item = !$need_remove[$j]; for ($k = $j + 1; $k < ($words_count - 1); $k += 2) { if ($words[$k] != $words[$k + 2]) { $need_remove_item = false; break; } } if ($need_remove_item) { for ($k = $j + 2; $k < $words_count; $k++) { $need_remove[$k] = true; } } } $result_string = ''; for ($i = 0; $i < $words_count; $i++) { if (!$need_remove[$i]) { $result_string .= ' ' . $words[$i]; } } return trim($result_string); } $string = '2 questions 3 questions 4 questions 5 questions'; echo $string . '<br>'; echo split_repeating($string) . '<br>'; echo '2 questions'; ?> 

    Если это помогает, \1 , \2 и т. Д. Используются для ссылки на предыдущую группировку. так, например, следующее выберет повторяющиеся слова и повторит их только один раз:

     $string =~ s/(\w+) ( \1)+/$1/g 

    Повторяющиеся фразы могут быть аналогично поставлены.

    Интересный вопрос. Это можно решить с помощью одного preg_replace() но длина повторяющейся фразы должна быть ограничена, чтобы избежать чрезмерного возврата. Вот решение с запрограммированным регулярным выражением, которое работает для тестовых данных и исправляет удвоенные, утроенные (или повторяющиеся n раз) фразы с максимальной длиной 50 символов:

    Решение части 1:

     $result = preg_replace('/ # Match a doubled "phrase" having length up to 50 chars. ( # $1: Phrase having whitespace boundaries. (?<=\s|^) # Assert phrase preceded by ws or BOL. \S # First char of phrase is non-whitespace. .{0,49}? # Lazily match phrase (50 chars max). ) # End $1: Phrase (?: # Group for one or more duplicate phrases. \s+ # Doubled phrase separated by whitespace. \1 # Match duplicate of phrase. ){1,} # Require one or more duplicate phrases. /x', '$1', $text); 

    Обратите внимание, что с помощью этого решения фраза может состоять из одного слова, и есть законные случаи, когда удвоенные слова являются правильной грамматикой и не должны быть исправлены. Если вышеупомянутое решение не является желаемым поведением, регулярное выражение может быть легко изменено, чтобы определить «фразу» как два или более «слова».

    Изменить: Изменено над регулярным выражением для обработки любого количества повторений фразы. Также добавлено решение второй части вопроса ниже.

    И вот аналогичное решение, в котором фраза начинается со слова цифр, а повторяющиеся фразы также должны начинаться со слова цифр (но первое слово цифр повторения фразы не обязательно должно совпадать с оригиналом):

    Решение части 2:

     $result = preg_replace('/ # Match doubled "phrases" with wildcard digits first word. ( # $1: 1st word of phrase (digits). \b # Anchor 1st phrase word to word boundary. \d+ # Phrase 1st word is string of digits. \s+ # 1st and 2nd words separated by whitespace. ) # End $1: 1st word of phrase (digits). ( # $2: Part of phrase after 1st digits word. \S # First char of phrase is non-whitespace. .{0,49}? # Lazily match phrase (50 chars max). ) # End $2: Part of phrase after 1st digits word. (?: # Group for one or more duplicate phrases. \s+ # Doubled phrase separated by whitespace. \d+ # Match duplicate of phrase. \s+ # Doubled phrase separated by whitespace. \2 # Match duplicate of phrase. ){1,} # Require one or more duplicate phrases. /x', '$1$2', $text); 

    Хорошая старая брутфорс …

    Это настолько уродливо, что я склонен публиковать его как eval(base64_decode(...)) , но вот он:

     function fixi($str) { $a = explode(" ", $str); return implode(' ', fix($a)); } function fix($a) { $l = count($a); $len = 0; for($i=1; $i <= $l/2; $i++) { for($j=0; $j <= $l - 2*$i; $j++) { $n = 1; $found = false; while(1) { $a1 = array_slice($a, $j, $i); $a2 = array_slice($a, $j+$n*$i, $i); if ($a1 != $a2) break; $found = true; $n++; } if ($found && $n*$i > $len) { $len = $n*$i; $f_j = $j; $f_i = $i; } } } if ($len) { return array_merge( fix(array_slice($a, 0, $f_j)), array_slice($a, $f_j, $f_i), fix(array_slice($a, $f_j+$len, $l)) ); } return $a; } 

    Пунктуация является частью слова, поэтому не ожидайте чудес.

    2 вопроса 3 вопроса 4 вопроса 5 вопросов

    становление

    2 вопроса

    Может быть решена с использованием:

     $string =~ s/(\d+ (.*))( \d+ (\2))+/$1/g; 

    Он соответствует числу, за которым следует что-либо (жадно), а затем ряд вещей, начинающихся с пробела, за которым следует число, за которым следует что-то, что соответствует предыдущему. При всем этом он заменяет его первым номером любой пары.

    ((?:\b|^)[\x20-\x7E]+)(\1)+ будет соответствовать любой повторяющейся строке печатаемых символов ASCII, начинающихся с границы слова. Это означает, что он будет hello hello но не двойной.

    Если вы хотите настроить символы, которые будут соответствовать, вы можете изменить и добавить диапазоны в форме \x##-\x##\x##-\x## (где ## – шестнадцатеричное значение) и опустить -\x## где вы просто хотите добавить одного персонажа.

    Единственная проблема, которую я вижу, заключается в том, что этот несколько простой подход мог бы выбрать законно повторяющееся слово, а не повторенную фразу. Если вы хотите заставить его ((?:\b|^)[\x20-\x7E]+\s)(\1)+ только повторяющиеся фразы, состоящие из нескольких слов, вы можете использовать что-то вроде ((?:\b|^)[\x20-\x7E]+\s)(\1)+ (обратите внимание на extra \s ).

    ((?:\b|^)[\x20-\x7E]+\s)(.*(\1))+ приближается к решению вашей второй проблемы, но я, возможно, подумал, что я оказался в углу на этом ,

    Изменить: просто для уточнения, вы должны использовать $string ~= /((?:\b|^)[\x20-\x7E]+\s)(.*(\1))+/$1/ig в Perl или эквивалент PHP, чтобы использовать это.

    Большое спасибо за ответ на вопрос. Это мне очень помогло!. Я пробовал как Ridgerunners, так и dtanders регулярные выражения, и хотя они работали (с некоторыми изменениями) на некоторых тестовых строках, у меня были проблемы с другими строками.

    Поэтому я отправился на атаку грубой силы :), которая вдохновлена ​​Nox. Таким образом, я мог бы объединить обе проблемы и по-прежнему иметь хорошую производительность (даже лучше, чем регулярное выражение, так как это медленное в PHP).

    Для всех заинтересованных здесь есть код концепции:

     function split_repeating_num($string) { $words = explode(' ', $string); $all_words = $words; $num_words = count($words); $max_length = 100; //max length of substring to check $max_words = 4; //maximum number of words in substring $found = array(); $current_pos = 0; $unset = array(); foreach ($words as $key=>$word) { //see if this word exist in the next part of the string $len = strlen($word); if ($len === 0) continue; $current_pos += $len + 1; //+1 for the space $substr = substr($string, $current_pos, $max_length); if (($pos = strpos(substr($string, $current_pos, $max_length), $word)) !== false) { //found it //set pointer words and all_words to same value while (key($all_words) < $key ) next($all_words); while (key($all_words) > $key ) prev($all_words); $next_word = next($all_words); while (is_numeric($next_word) || $next_word === '') { $next_word = next($all_words); } // see if it follows the word directly if ($word === $next_word) { $unset [$key] = 1; } elseif ($key + 3 < $num_words) { for($i = $max_words; $i > 0; $i --) { $x = 0; $string_a = ''; $string_b = ''; while ($x < $i ) { while (is_numeric($next_word) || $next_word === '' ) { $next_word = each($all_words); } $x ++; $string_a .= $next_word; $string_b .= $words [key($all_words) + $i]; } if ($string_a === $string_b) { //we have a match for($x = $key; $x < $i + $key; $x ++) $unset [$x] = 1; } } } } } foreach ($unset as $k=>$v) { unset($words [$k]); } return implode(' ', $words); 

    }

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