3

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

$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) =) :} :^)", '[悲伤] ' => ":'-( :'( :'-) :')" //等等...

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

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

4

5 回答 5

5

这是使用来自 CPAN的 Perl 3rd-party 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 已经知道会自动为您执行此操作。但是了解如何使用该模块仍然很有用,因此您可以生成用于其他语言的模式。

于 2012-02-15T16:17:55.407 回答
3

这听起来像是正则表达式的一个很好的应用程序,它是一个模糊文本匹配和替换的工具。str_replace是用于精确文本搜索和替换的工具;正则表达式将让您搜索“看起来像这样的文本”的整个类,其中this是根据您将接受的字符类型、数量、顺序等定义的。

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

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

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

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

    例如(:\)|:-\)|:o\))将匹配任何:),:-),:o). 请注意,我必须对)'s 进行转义,因为它们在正则表达式中具有特殊含义(括号用作分组运算符。)

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

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

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

注意:我的建议与编程语言无关。我的 PHP-fu 比我的 Python-fu 弱得多,所以我不能提供示例代码。:(

于 2012-02-15T15:19:43.343 回答
2

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

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

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

于 2012-02-15T15:26:35.887 回答
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 是最有效的方法吗?

好吧,现在使用您提供的代码,这是您询问的唯一方式。由于您告诉我们没有其他选择,它至少对您有用,这是目前最有效的方式。所以现在,是的。

于 2012-02-15T15:41:44.420 回答
2

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

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

并且当您填写所有查找/替换定义时,您应该以某种方式重新排序此数组,这样就没有机会将 :-)) 替换为 :-)。我相信如果您按长度对数组值进行排序就足够了。这是为了以防您要使用 str_replace()。strtr() 将自动按长度排序!

如果您担心性能,可以检查strtr 与 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
于 2012-02-15T16:20:58.140 回答