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

Posted

技术标签:

【中文标题】替换给定 unicode 字符串中的所有表情符号【英文标题】:Replace all emojis from a given unicode string 【发布时间】:2019-04-22 08:02:10 【问题描述】:

我有一个来自 emoji 包的 unicode 符号列表。我的最终目标是创建一个函数,将 unicode 字符串作为输入,即some????????thing,然后删除所有表情符号,即"something"。以下是我想要实现的目标的演示:

from emoji import UNICODE_EMOJI
text = 'some????????thing'
exclude_list = UNICODE_EMOJI.keys()
output = ... = 'something'

我一直在尝试执行上述操作,在此过程中,我遇到了一个奇怪的行为,如下所示,如您所见。我相信如果下面的代码是固定的,那么我将能够实现我的最终目标。

import regex as re
print u'\U0001F469'                     # ????   
print u'\U0001F60C'                     # ????    
print u'\U0001F469\U0001F60C'           # ???????? 

text = u'some\U0001F469\U0001F60Cthing' 
print text                              # some????????thing

# Removing "????????" works
print re.sub(ur'[\U0001f469\U0001F60C]+', u'', text)  # something
# Removing only "????" doesn't work 
print re.sub(ur'[\U0001f469]+', u'', text)            # some�thing

【问题讨论】:

我可以在 Mac OS X 10.13.6 上的 Python 2.7.10 中重现这个错误。在print 语句中插入repr 表明re.sub 的结果是u'some\ude0cthing'。 (顺便说一下,sys.maxunicode 是 65535。) Python 2 对 BMP 之外的字符没有很好的 Unicode 支持。如果你真的需要它,你需要一个带有 32 位 Unicode 字符的 Python 版本。否则开始使用更高版本的 Python 3。 @jwodder:打败我????我敢打赌,它们在内部存储为 2 字节 代理字符re 也将它们视为 2 个单独的字符 - 即删除 \u0001 的第一个字符。 哦等等,不是\u0001——它从不看到这些。它首先替换正确的代理,然后 然后 替换这些。由[..] 括起来,一次一个,首先是高位,然后是低位。 向我尖叫文本格式错误。将所有符合 unicode 的文本转换为 ascii 然后搜索适当的字符串会不会令人望而却步?不优雅,但可能会解决 2 字节代理问题? 【参考方案1】:

在大多数 Python 2.7 版本中,高于 0x10000 的 Unicode 代码点被编码为 surrogate pair,这意味着 Python 实际上将它们视为两个字符。您可以通过 len(u'\U0001F469') 向自己证明这一点。

解决此问题的最佳方法是迁移到将这些代码点正确视为单个实体而不是代理对的 Python 版本。您可以为此编译 Python 2.7,最新版本的 Python 3 会自动完成。

要创建用于replace 的正则表达式,只需将所有字符与| 连接在一起。由于字符列表已经使用代理对进行编码,因此它将创建正确的字符串。

subs = u'|'.join(exclude_list)
print re.sub(subs, u'', text)

【讨论】:

我不知道 2.7 可以用 4 字节 unicode 支持编译,我绝对不知道 Ubuntu 正在分发启用该功能的 python2.7。每天学习新东西。 @CJ59 这是为 *** 做出贡献的最佳理由之一,学习的机会是无穷无尽的。几乎没有一天我自己不学习新东西。【参考方案2】:

旧的 2.7 正则表达式引擎会感到困惑,因为:

    Python 2.7 使用强制基于单词的 Unicode 存储,其中某些 Unicode 代码点会自动替换为代理对。

    在正则表达式“看到”您的 Python 字符串之前,Python 已经帮助您将大型 Unicode 代码点解析为两个单独的字符(每个字符本身都是一个有效但不完整的单个 Unicode 字符)。

    李>

    这意味着[\U0001f469]+' 替换了 something(2 个字符的字符类),但其中一个在您的字符串中,另一个不在。这会导致您的输出格式错误。

这解决了它:

print re.sub(ur'(\U0001f469|U0001F60C)+', u'', text)  # something
# Removing only "?" doesn't work 
print re.sub(ur'(\U0001f469)+', u'', text)            # some�thing
# .. and now it does:
some?thing

因为现在正则表达式引擎会看到您正在寻找的完全相同的字符序列(代理对或其他)。

如果您想从exclude_list 中删除所有表情符号,您可以显式循环其内容并一一替换:

exclude_list = UNICODE_EMOJI.keys()

for bad in exclude_list:  # or simply "for bad in UNICODE_EMOJI" if you gotta catch them all
    if bad in text:
        print 'Removing '+bad
        text = text.replace(bad, '')
Removing ?
Removing ?
something

(这也显示了中间结果作为它有效的证明;您只需要循环中的replace 行。)

【讨论】:

是的,从文字创建正则表达式效果很好。这个问题表达了从字符列表中生成表达式的愿望,结果证明这要困难得多。我还没破解呢。【参考方案3】:

要使用当前方法从输入字符串中删除所有表情符号,请使用

import re
from emoji import UNICODE_EMOJI
text = u'some??thing'
exclude_list = UNICODE_EMOJI.keys()
rx = ur"(?:)+".format("|".join(map(re.escape,exclude_list)))
print re.sub(rx, u'', text)
# => u'something'

如果您不re.escape 表情符号字符,您将收到nothing to repeat 错误,因为文字字符与组内的交替运算符混淆,因此需要map(re.escape,exclude_list)

在 Python 2.7.12 中测试(默认,2018 年 11 月 12 日,14:36:49) [GCC 5.4.0 20160609] 在 linux2 上。

【讨论】:

以上是关于替换给定 unicode 字符串中的所有表情符号的主要内容,如果未能解决你的问题,请参考以下文章

从 Unicode 字符串中正确提取表情符号

使用 Spring 应用程序中的休眠将表情符号 unicode 字符保存在 mysql 数据库中

PHP:如何匹配一系列 unicode 配对代理表情符号/表情符号?

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

如何在不使用任何形式的查找表的情况下在 Swift for iOS 8 中列出(几乎)所有表情符号?

表情符号的 Python unicode 字符转换