从Unicode字符串中正确提取Emojis

Aar*_*ron 21 python unicode python-2.x emoji

我在Python 2中工作,我有一个包含emojis以及其他unicode字符的字符串.我需要将其转换为列表,其中列表中的每个条目都是单个字符/表情符号.

x = u'xyz'
char_list = [c for c in x]
Run Code Online (Sandbox Code Playgroud)

所需的输出是:

['', '', 'x', 'y', 'z', '', '']
Run Code Online (Sandbox Code Playgroud)

实际输出是:

[u'\ud83d', u'\ude18', u'\ud83d', u'\ude18', u'x', u'y', u'z', u'\ud83d', u'\ude0a', u'\ud83d', u'\ude0a']
Run Code Online (Sandbox Code Playgroud)

如何实现所需的输出?

iva*_*eev 17

首先,在Python2中,您需要使用Unicode字符串(u'<...>')将Unicode字符视为Unicode字符.而正确的信源编码,如果你想使用的字符本身,而不是\UXXXXXXXX在源代码表示.

现在,根据Python:当它包含代理项对时获取正确的字符串长度并且Python为单个Unicode字符串返回长度2,在Python2"narrow"构建(with sys.maxunicode==65535)中,32位Unicode字符表示为代理项对,并且这对字符串函数不透明.这仅在3.3(PEP0393)中修复.

最简单的解决方案(除了迁移到3.3+之外)是从源代码编译Python"宽"构建,如第3个链接所述.在其中,Unicode字符都是4字节(因此是潜在的内存占用),但如果您需要定期处理宽Unicode字符,这可能是一个可接受的价格.

"窄"构建的解决方案创建一组自定义字符串函数(len,slice可能作为子类unicode)来检测代理对并将它们作为单个字符处理.我不能轻易找到现有的(这很奇怪),但写起来并不难:

  • 根据UTF-16#U + 10000到U + 10FFFF - 维基百科,
    • 第一个字符(高代理人)在范围内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
Run Code Online (Sandbox Code Playgroud)

还有一个返回简单切片的函数:

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]
Run Code Online (Sandbox Code Playgroud)

在这里,您支付的价格是性能,因为这些函数比内置函数慢得多:

>>> 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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,对于表情符号的所有最近花哨的添加,这稍微打破了,因为一些表情符号由多个代码点组成.例子包括flags(`""`)和etnical variants(`""`vs`""`),还有其他一些东西,比如变音符号"à"`. (2认同)
  • 我认为规范化不会处理那些表情符号.严格正确的答案将在[Unicode®标准附件#29](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules)中迭代字形集群,冗长而神秘的解释.但是如果没有可以处理的库,我可能会坚持迭代代码点. (2认同)

Jam*_*kin 10

我会使用uniseg库(pip install uniseg):

# -*- coding: utf-8 -*-
from uniseg import graphemecluster as gc

print list(gc.grapheme_clusters(u'xyz'))
Run Code Online (Sandbox Code Playgroud)

输出[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'))]
Run Code Online (Sandbox Code Playgroud)

将提供字符列表为UTF-8编码的字符串.