【问题标题】:Match and replace emoticons in string - what is the most efficient way?匹配和替换字符串中的表情符号 - 最有效的方法是什么?
【发布时间】:2012-03-06 22:37:57
【问题描述】:

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

所以原则上这是可行的。但是,我有两个问题:

    1234563有没有办法在没有空格的情况下存储表情符号,但仍然与周围有空格的 $string 匹配? (和现在的代码一样高效?)
  1. 或者有没有办法将表情符号放在一个变量中,然后在空间上爆炸以检查 $string?类似的东西

    $表情符号 = 数组( '[HAPPY]' => ">:] :-) :) :o) :] :3 :c) :> =] 8) =) :} :^)", '[SAD]' => ":'-( :'( :'-) :')" //etc...

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

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

【问题讨论】:

  • 你不应该也处理trans-ASCII emotica吗?你知道,网络现在超过 80% 的 Unicode。有一个完整的 Unicode 块专门用于此类事情:Blk=Emoticons。但有些也发生在其他地方。
  • @Li-aungYip Heh,这很好!不,我的意思是像 U+1F609 WINKING FACE 这样的代码点 ????和 U+263A WHITE SMILING FACE ☺。它们中的大多数都在 Emotions 块中(就像上面两个中的第一个),只有少数在旧版 BMP 中。
  • @tchrist 不,我只关心en.wikipedia.org/wiki/List_of_emoticons 上定义的西方表情符号,但感谢您的输入:)
  • 不幸的是,默认的 Ubuntu 字体还没有包含该块中的所有代码点,所以我得到 U+263A 很好,但 U+1F609 是一个盒子。我想知道Win7的支持如何? ;)
  • 这里在 Win7 上没有眨眼。没有合适的后备字体,是问题所在。

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


【解决方案1】:

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

【讨论】:

  • @Li-aungYip 还有很多来自哪里;你必须记住whom you’re talking to,你知道的。
  • OH, SHI...(至少您不是 Mastering Regular Expressions 的作者。现在我将不得不留意这里的 jfriedl...)
  • @Li-aungYip 要真正掌握正则表达式,您需要现代模式的东西,而 Jeffrey 的 MRE 还没有涵盖这些东西。请参阅 this answer 了解我的意思:命名组(并且比 Python 获得的更灵活)、递归模式和语法模式。
【解决方案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

【讨论】:

  • 我以前从未遇到过:-))。这是什么意思?
  • 事实证明,非常快乐的人们使用它。在提供的 Wikipedia 列表中找到它,似乎 @Reveller 用作参考。我也不知道那个表情符号,可能是因为我什至用 :) 表达了我最强烈的情绪
【解决方案3】:

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

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

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

【讨论】:

  • 后缀树/有限状态机将是一个非常优雅的解决方案。荣誉。 (但在这种情况下,它不是前缀树吗?;))
  • 不,它是一个后缀树。维基百科页面显示了单词BANANA$ 的后缀树,其中“从根到叶的六个路径(显示为框)对应于六个后缀 A$、NA$、ANA$、NANA$、ANANA $和香蕉$"
  • 计算机科学,自 dynamic programming 以来给事物起不直观的名称(不是真正的编程类型。)
  • 您想使用 Perl Regexp::Assemble 模块对模式集进行分析,以创建前缀/后缀树表示作为正则表达式。然后,您可以将生成的优化正则表达式插入任何编程语言。这对于那些不够聪明而无法像 Perl 那样使用 TRIE 表示的语言特别有用。
  • @tchrist:将其归结为正则表达式可以让您区分您匹配的表情符号吗?即你还能用[HAPPY]替换:),用[SAD]替换:(使用preg_replace()吗?
【解决方案4】:

简介评论:请一次只问一个问题。你会得到比这更好的答案。除此之外,如果您不向我们展示您迄今为止所做的指标,您将无法获得好的性能建议。

从您的代码中我可以看出,您执行了两次可以保存的字符串处理,将替换内容放入特定的空格中。你可以先用你的定义展开它:

$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]' =&gt; "&gt;:] :-) :) :o) :] :3 :c) :&gt; =] 8) =) :} :^)", '[SAD]' =&gt; ":'-( :'( :'-) :')" //etc...

当然,这是可能的,但您会遇到与 1 中相同的问题。

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

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

【讨论】:

  • 您肯定不希望将编译后的 C 代码烘焙到 PHP 应用程序中吗?可行,当然,但不适用于初学者或任何想要保持理智的人。
  • 其实 PHP 是一个 C 编译函数的接口。由于 OP 要求性能,我认为这个建议并不遥远。但是,我不建议将 C 语言融入 PHP 应用程序,但建议如果性能至关重要,他应该使用 C 语言来代替。但这只是答案中的一小部分,如果 OP 想要留在 PHP 中(正如您所做的那样),我在这里概述了正则表达式的替代方案。
  • 我的编程经验现在已经达到了一些 php - 我不会考虑为此目的编写编译位。假设我想优化 php 脚本环境中的性能 :) 但是感谢您的建议!
  • 如果您在同一脚本执行中多次使用相同的正则表达式模式,我猜preg_replace 在您的情况下是最快的。但你需要衡量一下,这样你才能知道。
【解决方案5】:

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

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

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

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2020-01-19
    • 2015-07-14
    • 2011-07-25
    • 1970-01-01
    • 2016-08-16
    • 2011-01-12
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多