从 Unicode 字符串中正确提取表情符号
Posted
技术标签:
【中文标题】从 Unicode 字符串中正确提取表情符号【英文标题】:Correctly extract Emojis from a Unicode string 【发布时间】:2016-05-26 01:19:02 【问题描述】:我在 Python 2 中工作,我有一个包含表情符号以及其他 unicode 字符的字符串。我需要将其转换为列表中的每个条目都是单个字符/表情符号的列表。
x = u'????????xyz????????'
char_list = [c for c in x]
想要的输出是:
['????', '????', 'x', 'y', 'z', '????', '????']
实际输出为:
[u'\ud83d', u'\ude18', u'\ud83d', u'\ude18', u'x', u'y', u'z', u'\ud83d', u'\ude0a', u'\ud83d', u'\ude0a']
我怎样才能达到想要的输出?
【问题讨论】:
我已将其作为超集问题的副本关闭。仔细阅读他们的答案。如果仍然不能解决您的问题,请edit 发帖以包含您的其他尝试。 我的问题与另一个问题不同,因为我正在处理包含表情符号和非表情符号字符的字符串。另外,我对计算表情符号不感兴趣,而是对获取所有字符的列表感兴趣。 字符串输入有 7 个字符,将 emoji 记为单个字符。我得到的输出列表中有 11 个条目。我需要得到一个输出列表,其中包含与输入字符串中的字符相对应的 7 个条目。 ***.com/questions/12907022/…的副本 @ivan_pozdeev:它必须是 Python 2,因为实际输出使用u'...'
字符串文字来表示值。然后确实强调了这个问题缺少实际的minimal reproducible example。要么缺少from __future__ import unicode_literals
,要么缺少x
字符串定义上的u
前缀。
【参考方案1】:
首先,在 Python2 中,您需要使用 Unicode 字符串 (u'<...>'
) 才能将 Unicode 字符视为 Unicode 字符。 correct source encoding 如果您想使用字符本身而不是源代码中的 \UXXXXXXXX
表示形式。
现在,根据Python: getting correct string length when it contains surrogate pairs 和Python returns length of 2 for single Unicode character string,在Python2“窄”构建(使用sys.maxunicode==65535
)中,32 位Unicode 字符表示为surrogate pairs,这对字符串函数不透明。这仅在 3.3 (PEP0393) 中得到修复。
最简单的解决方案(迁移到 3.3+ 除外)是从源代码编译 Python“宽”构建,如第三个链接所述。其中,Unicode 字符都是 4 字节(因此可能会占用大量内存),但如果您需要定期处理宽 Unicode 字符,这可能是一个可以接受的价格。
“窄”构建的解决方案是制作一组自定义字符串函数(len
、slice
;可能作为@987654333 的子类@) 将检测代理对并将它们作为单个字符处理。我无法轻易找到现有的(这很奇怪),但写起来并不难:
0xD800..0xDBFF
范围内
第二个字符(低代理) - 在0xDC00..0xDFFF
范围内
这些范围是保留的,因此不能作为常规字符出现
下面是检测代理对的代码:
def is_surrogate(s,i):
if 0xD800 <= ord(s[i]) <= 0xDBFF:
try:
l = s[i+1]
except IndexError:
return False
if 0xDC00 <= ord(l) <= 0xDFFF:
return True
else:
raise ValueError("Illegal UTF-16 sequence: %r" % s[i:i+2])
else:
return False
还有一个返回简单切片的函数:
def slice(s,start,end):
l=len(s)
i=0
while i<start and i<l:
if is_surrogate(s,i):
start+=1
end+=1
i+=1
i+=1
while i<end and i<l:
if is_surrogate(s,i):
end+=1
i+=1
i+=1
return s[start:end]
在这里,您付出的代价是性能,因为这些函数比内置函数慢得多:
>>> ux=u"a"*5000+u"\U00100000"*30000+u"b"*50000
>>> timeit.timeit('slice(ux,10000,100000)','from __main__ import slice,ux',number=1000)
46.44128203392029 #msec
>>> timeit.timeit('ux[10000:100000]','from __main__ import slice,ux',number=1000000)
8.814016103744507 #usec
【讨论】:
请注意,随着最近对表情符号的所有花哨添加,这有点被破坏了,因为一些表情符号由多个代码点组成。示例包括标志 ("??"
) 和 etnical 变体 ("??"
vs "??"
),以及其他一些东西,例如组合变音符号 "à"
。
@roeland 然后is_surrogate
需要升级以检测这些并返回额外单词的数量(= 2 字节字符)而不是 True/False。前提是我们对这种情况感兴趣(如果您问我,控制字符和变音符号是完全不同的事情)并且其他设施(如规范化)无法完成任务。
我认为规范化不会处理这些表情符号。严格正确的答案将遍历Unicode® Standard Annex #29 中的字形簇、冗长而晦涩的解释。但是如果没有可以处理的库,我可能会坚持迭代代码点。
@roeland: 即使\X
正则表达式在一般情况下也无济于事,例如,某些(聊天)软件将:)
(U+003a U+0029) 显示为笑脸(图片) 即,它是给定上下文中的表情符号。
@J.F.Sebastian 哦,是的。曾几何时,我们输入了一个冒号和一个括号。真正老派的人也会输入破折号:-)。但我认为 OP 是在询问 Unicode 表情符号字符。【参考方案2】:
我会使用 uniseg 库 (pip install uniseg
):
# -*- coding: utf-8 -*-
from uniseg import graphemecluster as gc
print list(gc.grapheme_clusters(u'??xyz??'))
输出[u'\U0001f618', u'\U0001f618', u'x', u'y', u'z', u'\U0001f60a', u'\U0001f60a']
,和
[x.encode('utf-8') for x in gc.grapheme_clusters(u'??xyz??'))]
将以 UTF-8 编码字符串的形式提供字符列表。
【讨论】:
您的答案没有打印出所需的输出 好的,我将添加转换以准确提供问题所问的内容。 @James Hopkin 你能提供任何方式让我们将这些表情符号转换成 unicode,比如 ? 在 python 3 中转换成 u'\U0001f618' 您可以编写以下内容:'?'.encode('unicode_escape')
。虽然它产生字节,而不是字符串:b'\\U0001f618'
以上是关于从 Unicode 字符串中正确提取表情符号的主要内容,如果未能解决你的问题,请参考以下文章