如何在Python正则表达式中处理带有重音符号的字符?

33

我目前使用 re.findall 在字符串中查找并分离 '#' 字符后的单词,以获取哈希标签:

hashtags = re.findall(r'#([A-Za-z0-9_]+)', str1)

它搜索 str1 并找到所有的哈希标签。这个功能可以正常工作,但它不考虑像这些重音字符,例如: áéíóúñü¿

如果 str1 中有这些字母中的任何一个,它将保存直到该字母之前的哈希标记。因此,例如,#yogenfrüz 将变为 #yogenfr

我需要能够考虑德语、荷兰语、法语和西班牙语等所有重音字母,以便我可以保存像 #yogenfrüz 这样的哈希标记。

我应该如何处理?


2
使用 re.UNICODE 标志。 - Ashwini Chaudhary
1
@AshwiniChaudhary:UNICODE标志不会使使用的范围匹配非ASCII字符。如果您告诉正则表达式匹配a-z,它将采用字面范围,而不是人类解释aá是相同的事物。 - Martijn Pieters
@MartijnPieters:那么,它会做什么呢?;-) - JohnTortugo
@JohnTortugo:请查看下面的答案。 - Martijn Pieters
5个回答

34

请尝试以下方法:

hashtags = re.findall(r'#(\w+)', str1, re.UNICODE)

Regex101演示

编辑 请查看Martijn Pieters下面的有用评论。


11
小注意事项:\w 无法匹配组合字符,因此 aU+0301 COMBINING ACUTE ACCENT 将不会被匹配,尽管它们显示为 á。您可能希望先进行 NFC 正规化处理。 - Martijn Pieters
1
@MartijnPieters 感谢分享,总是有额外的东西可以学习。 - Ibrahim Najjar
@IbrahimNajjar你能否实现Martijn Pieters提到的修复方法到你的解决方案中?谢谢。 - Robert Valencia
2
@RobertValencia,除非你真的遇到了他描述的情况,否则我的解决方案仍然适用于带重音符号的字符。老实说,我不是Unicode专家,也不知道具体细节,但如果你想像他建议的那样进行规范化,请查看这个问题的其他答案。希望能帮到你。 - Ibrahim Najjar
有趣的是,我认为我正在经历@MartijnPieters描述的确切问题,只不过是é和e。我使用了Berk下面提出的解决方案,然后将字节对象解码回字符串。谢谢大家! - bddicken

19
我知道这个问题有点过时,但你也可以考虑将带重音的字符À(索引192)和ÿ(索引255)添加到你的原始正则表达式中。
hashtags = re.findall(r'#([A-Za-z0-9_À-ÿ]+)', str1)

这将返回['#yogenfrüz']
希望这对其他人有所帮助。

这个答案非常优雅。我从未想过可以使用这样的范围。谢谢你。 - ecv
那个 À-ÿ 前面的下划线是打错了吗? - ecv
1
感谢 @ecv,下划线来自于问题本身,他想在原始帖子的基础上包含一个下划线,我只是增加了重音字符的范围。 - zanga

4
你可能也希望使用这个功能。
import unicodedata
output = unicodedata.normalize('NFD', my_unicode).encode('ascii', 'ignore')

如何将所有转义字符转换为其相应的字符,例如如果有一个unicode à,如何将其转换为标准的a? 假设您已经将您的unicode加载到名为my_unicode的变量中... 将à规范化为a就这么简单...

import unicodedata output = unicodedata.normalize('NFD', my_unicode).encode('ascii', 'ignore') 显式示例...

myfoo = u'àà'
myfoo
u'\xe0\xe0'
unicodedata.normalize('NFD', myfoo).encode('ascii', 'ignore')
'aa'

请查看这个答案,它帮助了我很多:如何将带重音的Unicode字符转换为没有重音的纯ASCII字符?


我点了踩,因为这与 OP 想要的相反。他们想要考虑口音;因此,去除口音不是一个解决方案。 - bfontaine

0

在其他答案的基础上:

关键问题在于re模块与其他正则表达式引擎有显著的差异。理论上,Unicode对\w元字符的定义可以满足问题的要求,但是re模块没有实现Unicode的\w元字符。

简单的解决方案是交换正则表达式引擎,使用更兼容的解决方案。最简单的方法是安装regex模块并使用它。其他答案中给出的代码将按照问题的需求工作。

import regex as re
# import unicodedata as ud
import unicodedataplus as ud
hashtags = re.findall(r'#(\w+)', ud.normalize("NFC",str1))

或者,如果你只想关注拉丁文字母,包括非间距标记(即组合变音符号):

import regex as re
# import unicodedata as ud
import unicodedataplus as ud
hashtags = re.findall(r'#([\p{Latin}\p{Mn}]+)', ud.normalize("NFC",str1))

顺便说一下,我使用了unicodedataplus,它是unicodedata的替代品。它具有额外的方法,并且与Unicode版本保持最新。使用unicodedata模块需要更新Python以更新Unicode版本。


0
这是对Ibrahim Najjar原始回答的更新,基于Martijn Pieters对该回答的评论以及Martijn Pieters在https://dev59.com/NGQn5IYBdhLWcg3w7KvS#16467505中提供的另一个答案。
import re
import unicodedata

s = "#ábá123"
n = unicodedata.normalize('NFC', s)

print(n)
c = ''.join(re.findall(r'#\w+', n, re.UNICODE))
print(s, len(s), c, len(c))

@shabbir_khan 并非所有基本字符和组合变音符号的组合都具有预组合形式,即单词kɔ̈ɔ̈r在NFC、NFKC、NFKC_CF、NFD和NFLD中完全相同。 - Andj

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