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

Posted

技术标签:

【中文标题】匹配和替换字符串中的表情符号 - 最有效的方法是啥?【英文标题】: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 匹配? (和现在的代码一样高效?)

    或者有没有办法将表情符号放在一个变量中,然后在空间上爆炸以检查 $string?类似的东西

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

    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 上没有眨眼。没有合适的后备字体,是问题所在。 【参考方案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 是根据您将接受的字符类型、数量来定义的其中,以什么顺序等。

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

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

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

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

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

    过早的优化是万恶之源。

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

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

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

【讨论】:

以上是关于匹配和替换字符串中的表情符号 - 最有效的方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

用net匹配并替换iOS标准的emoji表情符号

替换给定 unicode 字符串中的所有表情符号

如何在golang中使用正则表达式替换字符串中的表情符号

数据库mysql 如何保存表情符号 然后返回到前端

C# 正则表达式匹配表情符号

从字符串中提取所有表情符号的正则表达式是啥?