正则表达式匹配表情符号

9
我们正在开发一个项目,希望用户能够使用表情符号语法(如:smile::heart::confused::stuck_out_tongue:),以及普通的表情符号(如:)<3:/:p)。
我在处理表情符号语法时遇到了麻烦,因为有些字符序列可能会出现在以下情况中:
  • 普通字符串或URL中 - http://example.com
  • 在表情符号语法中 - :pencil:
我该如何找到这些表情符号字符序列,但不包括其附近的其他字符?
我正在使用的整个正则表达式涵盖了所有表情符号,所以这里只是精简版。
(\:\)|\:\(|<3|\:\/|\:-\/|\:\||\:p)

您可以在此处玩耍它的演示:http://regexr.com/3a8o5

为什么不将其拆分为多个正则表达式?另外,您可以使用边界进行匹配,例如 /\b:\)\b/ - elclanrs
如果我没记错的话,TwemojiEmojione都提供了JS代码来使用它们的图像集,而且在Github、NPM、bower等平台上也有数十个相同功能的实现。 - Crissov
4个回答

7

首先匹配表情符号(以处理:pencil:这个例子),然后检查是否有终止空格或换行符:

(\:\w+\:|\<[\/\\]?3|[\(\)\\\D|\*\$][\-\^]?[\:\;\=]|[\:\;\=B8][\-\^]?[3DOPp\@\$\*\\\)\(\/\|])(?=\s|[\!\.\?]|$)

这个正则表达式会匹配以下内容(首选emoji),并将匹配结果保存在第一个匹配组中:

:( :) :P :p :O :3 :| :/ :\ :$ :* :@
:-( :-) :-P :-p :-O :-3 :-| :-/ :-\ :-$ :-* :-@
:^( :^) :^P :^p :^O :^3 :^| :^/ :^\ :^$ :^* :^@
): (: $: *:
)-: (-: $-: *-:
)^: (^: $^: *^:
<3 </3 <\3
:smile: :hug: :pencil:

此外,它还支持终端标点符号作为分隔符,除了空格。

您可以在此处查看更多详细信息并进行测试:https://regex101.com/r/aM3cU7/4


是的!我已经让表情符号选择起作用了,但关键是在现有正则表达式的末尾添加 (?=\s|[\!\.\,\?]|$)。谢谢! - FiniteLooper
3
在匹配 () 时,您还应该检查它是否不是一个有效的括号集合的一部分,例如,您不希望这与此处的 8) 表情符号匹配:blah blah bug (reproduced on iOS 8)。简而言之,这确实不是您可以很好地使用正则表达式来处理的事情。 - Abhi Beckert
看起来任何跟着一个 : 的字符都会匹配 (A:, ~:, H: 等等)。我想这不是你想要的。同时也会错过一组重复表情符号中除了最后一个以外的所有表情 ((:(:(:(::);):))。 - ruffin

2
制作一个对空格的正向预测
([\:\<]-?[)(|\\/pP3D])(?:(?=\s))
 |       |      |         |
 |       |      |         |
 |       |      |         |-> match last separating space
 |       |      |-> match last part of the emot
 |       |-> it may have a `-` or not 
 |-> first part of the emoticon

由于您正在使用JavaScript,并且无法访问环视:

/([\:\<]-?[)|\\/pP3D])(\s|$)/g.exec('hi :) ;D');

然后只需将结果数组的最后一个元素(很可能是空格)剔除即可使用 splice() 函数。

1
我会翻译以下内容:

我假设这些表情符号通常会在前后使用空格。那么\s可能是你要找的,因为它代表了一个空白字符。

那么你的正则表达式将变成:

\s+(\:\)|\:\(|<3|\:\/|\:-\/|\:\||\:p)\s

0

你想要关于空格的正则表达式环视。这里的另一个答案建议使用正向先行断言,但我会选择双重否定:

(?<!\S)(\:\)|\:\(|<3|\:\/|\:-\/|\:\||\:p)(?!\S)

虽然JavaScript不支持(?<!pattern),但是回顾前瞻可以被模拟

test_string.replace(/(\S)?(\:\)|\:\(|<3|\:\/|\:-\/|\:\||\:p)(?!\S)/,
                    function($0, $1) { return $1 ? $0 : replacement_text; });

我所做的只是在你的代码前面加上(?<!\S),并在后面加上(?!\S)。前缀确保您不会跟随非空格字符,因此唯一有效的前导条目是空格或无内容(行首)。后缀也是如此,确保您不会被非空格字符跟随。另请参阅更彻底的正则表达式演练
对问题本身的评论之一建议使用\b(单词边界)标记。我不建议这样做。实际上,这个建议会产生与您想要的相反的效果;\b:/确实会匹配http://,因为p:之间有一个单词边界。这种推理会建议使用\B(不是单词边界),例如\B:/\B。这更具可移植性(它适用于几乎所有正则表达式解析器,而look-around则不是),在这种情况下您可以选择它,但我更喜欢look-around。

我正在使用JavaScript,但JS不支持lookbehinds :( - FiniteLooper
@ChrisBarr 嗯,说得好。使用一个函数来替换调用即可解决。请看我刚刚所做的修改。 - Adam Katz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接