匹配并替换字符串中的表情符号 - 最有效的方法是什么?

时间:2012-02-15 15:12:12

标签: php regex performance string-matching suffix-tree

Wikipedia定义了许多人们可以使用的表情符号。我想将此列表与字符串中的单词匹配。我现在有这个:

$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]'=> “:' - (:'(:' - ):')”//等......

  3. str_replace是最有效的方法吗?

  4. 我问,因为我需要检查数百万字符串,所以我正在寻找最有效的方法来节省处理时间:)

5 个答案:

答案 0 :(得分:4)

这是使用CPAN的Perl第三方Regexp::Assemble模块的想法。例如,鉴于此计划:

#!/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的现代版本已经知道会自动为你做这件事。但是知道如何使用模块仍然很有用,这样你就可以生成在其他语言中使用的模式。

答案 1 :(得分:3)

这听起来像是正则表达式的一个很好的应用程序,它是模糊文本匹配和替换的工具。 str_replace完全文本搜索和替换的工具;正则表达式将允许您搜索整个类“看起来像 this ”的文本,其中 this 是根据您将接受的字符类型,有多少来定义的他们,以什么顺序等等。

如果您使用正则表达式,那么......

  1. \s通配符将匹配空格,因此您可以匹配\s$emotion\s

    (还要考虑表情符号出现在字符串末尾的情况 - 即that was funny lol :) - 您不能总是假设表情符号周围会有空格。您可以编写一个处理此表情符号的正则表达式。)< / p>

  2. 您可以编写一个匹配列表中任何表情符号的正则表达式。您可以使用替换符号|执行此操作,您可以将其视为OR符号。语法为(a|b|c)以匹配模式abc

    例如,(:\)|:-\)|:o\))将与:),:-),:o)中的任何一个匹配。请注意,我必须转义),因为它们在regexp中具有特殊含义(括号用作分组运算符。)

  3. 过早优化是万恶之源。

    首先尝试最明显的事情。如果这不起作用,您可以稍后对其进行优化(在您对代码进行分析之后,确保这确实会给您带来切实的性能优势。)

  4. 如果您想学习正则表达式,请尝试TextWrangler manual的第8章。这是对正则表达式的用法和语法的非常容易理解的介绍。

    注意:我的建议是独立于编程语言。我的PHP-fu比我的Python-fu弱得多,所以我无法提供示例代码。 :(

答案 2 :(得分:2)

我会先使用str_replace和那些带空格的数组开始尝试最简单的实现。如果表现不可接受,请为每种情绪尝试一个正则表达式。这压缩了很多东西:

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

如果性能仍然不可接受,你可以使用更高级的东西,比如后缀树(参见:http://en.wikipedia.org/wiki/Suffix_tree),它允许你只扫描所有表情符号的字符串一次。这个概念很简单,你有一个树,它的根是一个空格(因为你想在表情符号之前匹配一个空格),第一个孩子是':'和'=',那么':'的孩子是']', ')',' - '等。你有一个循环扫描字符串,char由char。当你找到一个空格时,你移动到树中的下一个级别,然后查看下一个字符是否是该级别的节点之一(':'或'='),如果是,则移动到下一个级别,等等如果在任何时候,当前char不是当前级别的节点,则返回root。

答案 3 :(得分:2)

  

简介评论:请一次只提出一个问题。你会得到更好的答案。接下来,如果您没有向我们展示您迄今为止所做的指标,则无法获得良好的性能建议。

从我的代码中我可以看到,你做两次字符串处理可以保存,将替换放入特定的空格中。您可以先使用您的定义将其展开:

$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是最有效的方法吗?

现在使用您提供的代码,这是您询问的唯一方式。由于你没有别的选择告诉我们,它至少为你工作,在这个时间点,这是最有效的方式。所以现在,是的。

答案 4 :(得分:2)

如果您想要替换表情符号的$ string是由您网站的访问者提供的(我的意思是用户的输入,如评论或其他内容),那么您不应该传达之前或之后会有空格表情符号。还有至少几个表情符号,它们非常相似但不同,如:-)和:-))。 所以如果你定义你的表情符号数组,我认为你将获得更好的结果:

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

当你填写所有的查找/替换定义时,你应该以某种方式重新排序这个数组,没有机会用:-)取代:-))。我相信如果按长度排序数组值就足够了。这是为了你打算使用str_replace()。 strtr()会自动按长度排序!

如果您担心性能问题,可以查看strtr vs str_replace,但我建议您进行自己的测试(关于$ string长度和查找/替换定义,您可能得到不同的结果)。

最简单的方法是,如果“查找定义”不包含尾随空格:

$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