从 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 字符,这可能是一个可以接受的价格。

“窄”构建的解决方案制作一组自定义字符串函数lenslice;可能作为@987654333 的子类@) 将检测代理对并将它们作为单个字符处理。我无法轻易找到现有的(这很奇怪),但写起来并不难:

根据UTF-16#U+10000 to U+10FFFF - Wikipedia, 第一个字符(高代理)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 字符串中正确提取表情符号的主要内容,如果未能解决你的问题,请参考以下文章

如何从字符串中删除表情符号字符?

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

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

CSS 对手机表情符号字体的引用?

表情符号的 Python unicode 字符转换

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