如何在Python中正确迭代Unicode字符

4
我希望能够遍历一个字符串并输出其中所有的表情符号。
我试图遍历每个字符,并将其与 表情符号列表 进行比对。
然而,Python 似乎会将 Unicode 字符分割成更小的字符,从而破坏了我的代码。例如:
>>> list(u'Test \U0001f60d')
[u'T', u'e', u's', u't', u' ', u'\ud83d', u'\ude0d']

你有什么想法为什么u'\U0001f60d'会被分割?

或者有更好的方法提取所有表情符号吗?这是我的原始提取代码:

def get_emojis(text):
  emojis = []
  for character in text:
    if character in EMOJI_SET:
      emojis.append(character)
  return emojis

4
我无法在Python 2.7或Python 2.6上重现它(而我手头没有更旧的版本)。当我查看list(u'Test \ U0001f60d')时,我得到[u'T',u'e',u's',u't',u' ',u'\ U0001f60d']。你正在使用哪个版本的Python? - Alfe
这是宽Unicode字符在窄版本中的内部表示方式。这个问题应该在Python3.3+中得到解决,因为内部表示方式已经被改变 - mata
此外,您还可以翻转循环并迭代表情符号,而不是原始字符串。 - Yaroslav Surzhikov
3个回答

8
Python 3.3及以前版本内部使用UTF-16LE(窄版)或UTF-32LE(宽版)存储Unicode,并由于漏洞抽象leaky abstraction将此细节暴露给用户。 UTF-16LE使用surrogate pairs表示超过U+FFFF的Unicode字符为两个代码点。 要解决此问题,请使用宽Python构建或切换到Python 3.3或更高版本。
应对窄版的一种方法是匹配代理对:
Python 2.7(窄版):
>>> s = u'Test \U0001f60d'
>>> len(s)
7
>>> re.findall(u'(?:[\ud800-\udbff][\udc00-\udfff])|.',s)
[u'T', u'e', u's', u't', u' ', u'\U0001f60d']

Python 3.6:

>>> s = 'Test \U0001f60d'
>>> len(s)
6
>>> list(s)
['T', 'e', 's', 't', ' ', '']

我不知道为什么,但我认为它不适用于所有Unicode。请尝试“测试”。 - Tom Wojcik
1
@TomWojcik Unicode字符串由Unicode代码点组成,但是有些代码点与其他代码点组合形成图形符号(单个可视字符)。例如,标志由两个代码点组成。 - Mark Tolonen
TIL,谢谢。因此它可以正确地分割为(有时是多个)Unicode表示形式,但我认为OP需要图形符号(即最终用户所期望的表情符号)。 - Tom Wojcik
1
@TomWojcik 字形是一个复杂的话题,但如果我没记错的话,第三方的 regex 库有一个可以使用的 \g。不仅表情符号可以有多个代码点。Unicode 只会变得更加复杂。这篇文章已经五年了。 - Mark Tolonen

1

我一直在与Unicode斗争,发现它并没有看起来那么简单。

有一个表情符号库(作者与其无关)可以解决所有的问题。

如果您想列出字符串中出现的所有表情符号,则建议使用emoji.emoji_lis

只需查看emoji.emoji_lis的源代码即可了解其实际复杂程度。

示例

>>> emoji.emoji_lis('')
>>> [{'location': 0, 'emoji': ''}, {'location': 1, 'emoji': ''}, {'location': 2, 'emoji': ''}]

带列表的示例(不一定总是有效)

>>> list('')
>>> ['', '', '', '']

0

试试这个,

import re
re.findall(r'[^\w\s,]', my_list[0])

正则表达式r'[^\w\s,]'匹配任何不是单词、空格或逗号的字符。

仍然将表情符号拆分为两个字符:>>> re.findall(r'[^\w\s,]', u'Test \U0001f60d') [u'\ud83d', u'\ude0d'] - Vinicius Fortuna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接