代码之家  ›  专栏  ›  技术社区  ›  Aaron

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

  •  22
  • Aaron  · 技术社区  · 10 年前

    我在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']
    

    如何实现期望的输出?

    2 回复  |  直到 10 年前
        1
  •  17
  •   Community Mohan Dere    8 年前

    首先,在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 ; 可能是的一个子类 unicode )这将检测代理项对并将其作为单个字符处理。我很难找到一个现有的(这很奇怪),但写起来并不难:

    • 根据 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
    
        2
  •  10
  •   James Hopkin    10 年前

    我会使用 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编码字符串的字符列表。

    推荐文章