首先,这里根本不需要编码。你有一个 Unicode 字符串,re
引擎可以处理 Unicode,所以就使用它。
字符类可以包含一系列字符,方法是指定第一个字符和最后一个字符,并在它们之间加上连字符。并且您可以指定您不知道如何使用\U
转义序列键入的 Unicode 字符。所以:
import re
s=u"Smiley emoticon rocks!\U0001f600 I like you.\U0001f601"
count = len(re.findall(ru'[\U0001f600-\U0001f650]', s))
或者,如果字符串足够大以至于构建整个findall
列表似乎很浪费:
emoticons = re.finditer(ru'[\U0001f600-\U0001f650]', s)
count = sum(1 for _ in emoticons)
数单词,你可以分开做:
wordcount = len(s.split())
如果您想一次完成所有操作,可以使用交替组:
word_and_emoticon_count = len(re.findall(ru'\w+|[\U0001f600-\U0001f650]', s))
正如@strangefeatures 指出的那样,3.3 之前的 Python 版本允许“窄 Unicode”构建。而且,例如,大多数 CPython Windows 版本都很窄。在狭窄的构建中,字符只能在 to 范围U+0000
内U+FFFF
。没有办法搜索这些字符,但没关系,因为它们不存在可搜索;如果您在编译正则表达式时遇到“无效范围”错误,您可以假设它们不存在。
当然,除了很有可能无论您从何处获取实际字符串,它们都是 UTF-16-BE 或 UTF-16-LE,因此字符确实存在,它们只是被编码为代理对. 你想匹配那些代理对,对吧?因此,您需要将您的搜索转换为代理对搜索。也就是说,将您的高代码点和低代码点转换为代理对代码单元,然后(用 Python 术语)搜索:
(lead == low_lead and lead != high_lead and low_trail <= trail <= DFFF or
lead == high_lead and lead != low_lead and DC00 <= trail <= high_trail or
low_lead < lead < high_lead and DC00 <= trail <= DFFF)
如果您不担心接受伪造的 UTF-16,则可以在最后一种情况下省略第二个条件。
如果不清楚如何将其转换为正则表达式,这里有一个[\U0001e050-\U0001fbbf]
UTF-16-BE 范围的示例:
(\ud838[\udc50-\udfff])|([\ud839-\ud83d].)|(\ud83e[\udc00-\udfbf])
当然,如果您的范围足够小,low_lead == high_lead
这会变得更简单。例如,可以使用以下命令搜索原始问题的范围:
\ud83d[\ude00-\ude50]
最后一个技巧,如果您实际上不知道您将获得 UTF-16-LE 还是 UTF-16-BE(并且 BOM 与您正在搜索的数据相距甚远):因为没有代理线索或跟踪代码单元作为独立字符或一对的另一端有效,您可以双向搜索:
(\ud838[\udc50-\udfff])|([\ud839-\ud83d][\udc00-\udfff])|(\ud83e[\udc00-\udfbf])|
([\udc50-\udfff]\ud838)|([\udc00-\udfff][\ud839-\ud83d])|([\udc00-\udfbf]\ud83e)