Подбирайте и заменяйте смайлики в строке - какой самый эффективный способ? - PullRequest
3 голосов
/ 15 февраля 2012

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

$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 наиболее эффективным способом сделать это?

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

Ответы [ 5 ]

4 голосов
/ 15 февраля 2012

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

3 голосов
/ 15 февраля 2012

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

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

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

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

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

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

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

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

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

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

2 голосов
/ 15 февраля 2012

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

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

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

Если вас беспокоит производительность, вы можете проверить strtr vs str_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
2 голосов
/ 15 февраля 2012

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 - самый эффективный способ сделать это?

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

2 голосов
/ 15 февраля 2012

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...