Сопоставьте и замените смайлики в строке – что является наиболее эффективным способом?

Википедия определяет множество возможных смайликов, которые люди могут использовать. Я хочу сопоставить этот список со словами в строке. У меня теперь есть следующее:

$string = "Lorem ipsum :-) dolor :-| samet"; $emoticons = array( '[HAPPY]' => array(' :-) ', ' :) ', ' :o) '), //etc... '[SAD]' => array(' :-( ', ' :( ', ' :-| ') ); foreach ($emoticons as $emotion => $icons) { $string = str_replace($icons, " $emotion ", $string); } echo $string; 

Вывод:

 Lorem ipsum [HAPPY] dolor [SAD] samet 

так что в принципе это работает. Однако у меня есть два вопроса:

  1. Как вы можете видеть, я помещаю пробелы вокруг каждого смайлика в массиве, например ':-)' вместо ':-)' Это делает массив менее читаемым, на мой взгляд. Есть ли способ хранить смайлики без пробелов, но все же сопоставлять их с $ string с пробелами вокруг них? (и так же эффективно, как и код сейчас?)

  2. Или, возможно, есть способ разместить смайлики в одной переменной и взорваться в пространстве, чтобы проверить на $ string? Что-то вроде

    $ emoticons = array ('[HAPPY]' => ">:] 🙂 :): o):]: 3: c):> =] 8) =):}: ^)", '[SAD] '=> ":' – (: '(:' -): ')" // etc …

  3. Действительно ли str_replace наиболее эффективный способ сделать это?

Я спрашиваю, потому что мне нужно проверить миллионы строк, поэтому я ищу наиболее эффективный способ сохранить время обработки 🙂

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

     $emoticons = array( ':-)' => '[HAPPY]', ':)' => '[HAPPY]', ':o)' => '[HAPPY]', ':-(' => '[SAD]', ':(' => '[SAD]', ... ) 

    И когда вы заполняете все определения find / replace, вы должны переупорядочить этот массив таким образом, что не будет возможности заменить :-)) на :-). Я считаю, что если вы отсортируете значения массива по длине, будет достаточно. Это в случае, если вы собираетесь использовать str_replace (). strtr () автоматически выполнит эту сортировку по длине!

    Если вас беспокоит производительность, вы можете проверить strtr vs str_replace , но я предлагаю сделать собственное тестирование (вы можете получить другой результат в отношении длины $ string и определения find / replace).

    Самый простой способ – если ваши «определения поиска» не содержат конечных пробелов:

     $string = strtr( $string, $emoticons ); $emoticons = str_replace( '][', '', trim( join( array_unique( $emoticons ) ), '[]' ) ); $string = preg_replace( '/\s*\[(' . join( '|', $emoticons ) . ')\]\s*/', '[$1]', $string ); // striping white spaces around word-styled emoticons 

    Вот идея с использованием Perl стороннего модуля Regexp :: Assemble из CPAN. Например, учитывая эту программу:

     #!/usr/bin/env perl use utf8; use strict; use warnings; use Regexp::Assemble; my %faces = ( HAPPY => [qw¡ :-) :) :o) :-} ;-} :-> ;-} ¡], SAD => [qw¡ :-( :( :-| ;-) ;-( ;-< |-{ ¡], ); for my $name (sort keys %faces) { my $ra = Regexp::Assemble->new(); for my $face (@{ $faces{$name} }) { $ra->add(quotemeta($face)); } printf "%-12s => %s\n", "[$name]", $ra->re; } 

    Он выведет это:

     [HAPPY] => (?-xism:(?::(?:-(?:[)>]|\})|o?\))|;-\})) [SAD] => (?-xism:(?::(?:-(?:\||\()|\()|;-[()<]|\|-\{)) 

    Там немного лишних вещей, которые вам действительно не нужны, поэтому они уменьшатся до:

     [HAPPY] => (?:-(?:[)>]|\})|o?\))|;-\} [SAD] => (?:-(?:\||\()|\()|;-[()<]|\|-\{ 

    или так. Вы можете создать это в своей программе Perl, чтобы обрезать лишние биты. Затем вы можете поместить правые стороны прямо в ваш preg_replace .

    Причина, по которой я use utf8 заключалась в том, что я мог использовать ¡ как разделитель qw// , потому что я не хотел возиться с тем, что там скрывался.

    Вам не нужно было бы это делать, если бы вся программа была в Perl, потому что современные версии Perl уже знают, чтобы сделать это для вас автоматически. Но по-прежнему полезно знать, как использовать модуль, чтобы вы могли создавать шаблоны для использования на других языках.

    Это похоже на хорошее приложение для регулярных выражений, которые являются инструментом для нечеткого соответствия текста и замены. str_replace – инструмент для точного поиска текста и замены; regexps позволит вам искать целые классы «текст, который выглядит примерно так», где это определяется с точки зрения того, какие символы вы примете, сколько из них, в каком порядке и т. д.

    Если вы используете регулярные выражения, то …

    1. Обозначение \s будет соответствовать пробелу, поэтому вы можете сопоставить \s$emotion\s .

      (Также рассмотрим случай, когда смайлик встречается в конце строки, т. that was funny lol :) – вы не всегда можете предположить, что у смайликов будут пробелы вокруг них. Вы можете написать регулярное выражение, которое обрабатывает это.)

    2. Вы можете написать регулярное выражение, которое будет соответствовать любому из смайликов в списке. Вы делаете это, используя символ чередования | , которую вы можете прочитать как символ OR . Синтаксис (a|b|c) соответствует шаблону a OR b OR c .

      Например (:\)|:-\)|:o\)) будет соответствовать любому из :),:-),:o) . Обратите внимание, что мне пришлось скрыться от них ) , потому что они имеют особое значение внутри регулярных выражений (круглые скобки используются в качестве оператора группировки).

    3. Преждевременная оптимизация – это корень всего зла.

      Сначала попробуйте самое очевидное. Если это не сработает, вы можете оптимизировать его позже (после того, как вы профилируете код, чтобы убедиться, что это действительно даст вам ощутимое преимущество в производительности).

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

    Примечание: мой совет не зависит от языка программирования. Мой PHP-fu намного слабее моего Python-fu, поэтому я не могу предоставить пример кода. 🙁

    Intro Comment: Пожалуйста, задайте сразу один вопрос. Вы получите лучшие ответы, чем. Кроме того, вы не можете получить хороший совет по эффективности, если не покажете нам показатели, которые вы сделали до сих пор.

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

     $emoticons = array( ' [HAPPY] ' => array(' :-) ', ' :) ', ' :o) '), //etc... ' [SAD] ' => array(' :-( ', ' :( ', ' :-| ') ); foreach ($emoticons as $replace => $search) { $string = str_replace($search, $replace, $string); } 

    Это позволит сэкономить вам несколько долей микросекунды каждый раз, когда вы назовете то, что, хорошо дайте вам лучшую производительность, вы, вероятно, не заметите. Это подводит меня к тому, что вы, вероятно, должны написать это на C и скомпилировать его.

    Немного ближе к C будет использовать регулярное выражение, скомпилированное один раз, а затем повторно использованное, что уже было предложено в другом ответе. Преимущество здесь в том, что у вас может быть самый быстрый способ сделать это с помощью PHP, если вы выполняете одно и то же выражение несколько раз, и вы можете создать регулярное выражение вверх, чтобы вы могли сохранить его в удобном для вас формате. Затем вы можете кэшировать регулярное выражение, если вам нужно будет даже немного подстроить производительность.

    1. Как вы можете видеть, я помещаю пробелы вокруг каждого смайлика в массиве, например ':-)' вместо ':-)' Это делает массив менее читаемым, на мой взгляд. Есть ли способ хранить смайлики без пробелов, но все же сопоставлять их с $ string с пробелами вокруг них? (и так же эффективно, как и код сейчас?)

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

    2. Или есть ли способ разместить смайлики в одной переменной и взорваться на пробел для проверки на $ string? Что-то вроде

    $emoticons = array( '[HAPPY]' => ">:] :-) :) :o) :] :3 :c) :> =] 8) =) :} :^)", '[SAD]' => ":'-( :'( :'-) :')" //etc...

    Конечно, это возможно, но вы сталкиваетесь с теми же проблемами, что и в 1.

    3. Самый эффективный способ сделать это str_replace?

    Хорошо сейчас с кодом, который вы предложили, это единственный способ, о котором вы просите. Поскольку нет альтернативы, о которой вы нам рассказываете, это, по крайней мере, работает для вас, что на данный момент является наиболее эффективным способом сделать это для вас. Так что прямо сейчас, да.

    Сначала я str_replace простейшую реализацию, используя str_replace и те массивы с пробелами. Если производительность неприемлема, попробуйте создать одно регулярное выражение для каждой эмоции. Это сжимает вещи совсем немного:

     $emoticons = array( '[HAPPY]' => ' [:=]-?[\)\]] ', '[SAD]' => ' [:=]-?[\(\[\|] ' ); 

    Если производительность по-прежнему неприемлема, вы можете использовать что-то fancier, например дерево суффиксов (см. http://en.wikipedia.org/wiki/Suffix_tree ), что позволяет сканировать строку только один раз для всех смайликов. Концепция проста: у вас есть дерево, корень которого является пространством (так как вы хотите совместить пространство перед смайликом), первыми детьми являются «:» и «=», затем дети из:: are are], ')', '-' и т. д. У вас есть один цикл, который сканирует строку, char по char. Когда вы найдете пробел, вы переходите на следующий уровень в дереве, а затем видите, является ли следующий символ одним из узлов на этом уровне (':' или '='), если это так, перейдите на следующий уровень и т. Д. Если в любой точке текущий символ не является узлом текущего уровня, вы возвращаетесь к корню.