我在 Python 中有一个 Unicode 字符串,我想删除所有的重音符号(变音符号)。
我在网上找到了一种优雅的方法(在 Java 中):
- 将 Unicode 字符串转换为其长规范化形式(字母和变音符号使用单独的字符)
- 删除所有 Unicode 类型为“变音符号”的字符。
我是否需要安装诸如 pyICU 之类的库,或者仅使用 Python 标准库就可以做到这一点?那么python 3呢?
重要提示:我想避免使用从重音字符到非重音字符的显式映射的代码。
我在 Python 中有一个 Unicode 字符串,我想删除所有的重音符号(变音符号)。
我在网上找到了一种优雅的方法(在 Java 中):
我是否需要安装诸如 pyICU 之类的库,或者仅使用 Python 标准库就可以做到这一点?那么python 3呢?
重要提示:我想避免使用从重音字符到非重音字符的显式映射的代码。
Unidecode是这个问题的正确答案。它将任何 unicode 字符串转译为 ascii 文本中最接近的可能表示形式。
例子:
accented_string = u'Málaga'
# accented_string is of type 'unicode'
import unidecode
unaccented_string = unidecode.unidecode(accented_string)
# unaccented_string contains 'Malaga'and is of type 'str'
这个怎么样:
import unicodedata
def strip_accents(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
这也适用于希腊字母:
>>> strip_accents(u"A \u00c0 \u0394 \u038E")
u'A A \u0394 \u03a5'
>>>
字符类别“ Mn”代表Nonspacing_Mark
,类似于 MiniQuark 的答案中的 unicodedata.combining(我没有想到 unicodedata.combining,但它可能是更好的解决方案,因为它更明确)。
请记住,这些操作可能会显着改变文本的含义。口音、变音符号等不是“装饰”。
我刚刚在网上找到了这个答案:
import unicodedata
def remove_accents(input_str):
nfkd_form = unicodedata.normalize('NFKD', input_str)
only_ascii = nfkd_form.encode('ASCII', 'ignore')
return only_ascii
它工作得很好(例如法语),但我认为第二步(删除重音符号)比删除非 ASCII 字符处理得更好,因为这对于某些语言(例如希腊语)会失败。最好的解决方案可能是明确删除标记为变音符号的 unicode 字符。
编辑:这可以解决问题:
import unicodedata
def remove_accents(input_str):
nfkd_form = unicodedata.normalize('NFKD', input_str)
return u"".join([c for c in nfkd_form if not unicodedata.combining(c)])
unicodedata.combining(c)
c
如果字符可以与前面的字符组合,则返回 true ,主要是如果它是一个变音符号。
编辑 2:remove_accents
需要一个unicode字符串,而不是字节字符串。如果您有一个字节字符串,那么您必须将其解码为一个 unicode 字符串,如下所示:
encoding = "utf-8" # or iso-8859-15, or cp1252, or whatever encoding you use
byte_string = b"café" # or simply "café" before python 3.
unicode_string = byte_string.decode(encoding)
实际上,我在项目兼容的 python 2.6、2.7 和 3.4 上工作,我必须从免费用户条目中创建 ID。
多亏了你,我创造了这个奇迹般的功能。
import re
import unicodedata
def strip_accents(text):
"""
Strip accents from input String.
:param text: The input string.
:type text: String.
:returns: The processed String.
:rtype: String.
"""
try:
text = unicode(text, 'utf-8')
except (TypeError, NameError): # unicode is a default on python 3
pass
text = unicodedata.normalize('NFD', text)
text = text.encode('ascii', 'ignore')
text = text.decode("utf-8")
return str(text)
def text_to_id(text):
"""
Convert input text to id.
:param text: The input string.
:type text: String.
:returns: The processed String.
:rtype: String.
"""
text = strip_accents(text.lower())
text = re.sub('[ ]+', '_', text)
text = re.sub('[^0-9a-zA-Z_-]', '', text)
return text
结果:
text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889")
>>> 'montreal_uber_1289_mere_francoise_noel_889'
这不仅可以处理重音,还可以处理“笔画”(如 ø 等):
import unicodedata as ud
def rmdiacritics(char):
'''
Return the base character of char, by "removing" any
diacritics like accents or curls and strokes and the like.
'''
desc = ud.name(char)
cutoff = desc.find(' WITH ')
if cutoff != -1:
desc = desc[:cutoff]
try:
char = ud.lookup(desc)
except KeyError:
pass # removing "WITH ..." produced an invalid name
return char
这是我能想到的最优雅的方式(亚历克西斯在本页的评论中已经提到过),尽管我认为它确实不是很优雅。事实上,正如评论中所指出的,这更像是一种 hack,因为 Unicode 名称 - 实际上只是名称,它们不能保证是一致的或任何东西。
还有一些特殊的字母没有被这个处理,例如翻转和倒置的字母,因为它们的 unicode 名称不包含“WITH”。这取决于你想做什么。我有时需要去除重音来实现字典排序顺序。
合并了评论中的建议(处理查找错误、Python-3 代码)。
回应@MiniQuark 的回答:
我试图读取一个半法语(包含重音)的csv文件以及一些最终会变成整数和浮点数的字符串。作为测试,我创建了一个test.txt
如下所示的文件:
蒙特利尔, über, 12.89, Mère, Françoise, noël, 889
我必须包含行2
并3
让它工作(我在 python 票证中找到),以及合并@Jabba 的评论:
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
import csv
import unicodedata
def remove_accents(input_str):
nkfd_form = unicodedata.normalize('NFKD', unicode(input_str))
return u"".join([c for c in nkfd_form if not unicodedata.combining(c)])
with open('test.txt') as f:
read = csv.reader(f)
for row in read:
for element in row:
print remove_accents(element)
结果:
Montreal
uber
12.89
Mere
Francoise
noel
889
(注意:我在 Mac OS X 10.8.4 上使用 Python 2.7.3)
来自Gensim 的gensim.utils.deaccent(text) - 人类主题建模:
'Sef chomutovskych komunistu dostal postou bily prasek'
另一种解决方案是unidecode。
请注意,建议的unicodedata解决方案通常仅删除某些字符中的重音符号(例如,它变成'ł'
,''
而不是变成'l'
)。
import unicodedata
from random import choice
import perfplot
import regex
import text_unidecode
def remove_accent_chars_regex(x: str):
return regex.sub(r'\p{Mn}', '', unicodedata.normalize('NFKD', x))
def remove_accent_chars_join(x: str):
# answer by MiniQuark
# https://stackoverflow.com/a/517974/7966259
return u"".join([c for c in unicodedata.normalize('NFKD', x) if not unicodedata.combining(c)])
perfplot.show(
setup=lambda n: ''.join([choice('Málaga François Phút Hơn 中文') for i in range(n)]),
kernels=[
remove_accent_chars_regex,
remove_accent_chars_join,
text_unidecode.unidecode,
],
labels=['regex', 'join', 'unidecode'],
n_range=[2 ** k for k in range(22)],
equality_check=None, relative_to=0, xlabel='str len'
)
在我看来,建议的解决方案不应被接受。最初的问题是要求去除口音,所以正确的答案应该只这样做,而不是加上其他未指定的更改。
只需观察此代码的结果,即可接受的答案。我将“Málaga”更改为“Málagueña”:
accented_string = u'Málagueña'
# accented_string is of type 'unicode'
import unidecode
unaccented_string = unidecode.unidecode(accented_string)
# unaccented_string contains 'Malaguena'and is of type 'str'
还有一个额外的更改 (ñ -> n),在 OQ 中没有要求。
执行请求任务的简单函数,格式如下:
def f_remove_accents(old):
"""
Removes common accent characters, lower form.
Uses: regex.
"""
new = old.lower()
new = re.sub(r'[àáâãäå]', 'a', new)
new = re.sub(r'[èéêë]', 'e', new)
new = re.sub(r'[ìíîï]', 'i', new)
new = re.sub(r'[òóôõö]', 'o', new)
new = re.sub(r'[ùúûü]', 'u', new)
return new
一些语言结合了变音符号作为语言字母和重音变音符号来指定重音。
我认为明确指定要删除的变音符号更安全:
def strip_accents(string, accents=('COMBINING ACUTE ACCENT', 'COMBINING GRAVE ACCENT', 'COMBINING TILDE')):
accents = set(map(unicodedata.lookup, accents))
chars = [c for c in unicodedata.normalize('NFD', string) if c not in accents]
return unicodedata.normalize('NFC', ''.join(chars))
如果您希望获得类似于 Elasticsearchasciifolding
过滤器的功能,您可能需要考虑fold-to-ascii,它是 [本身]...
Apache Lucene ASCII 折叠过滤器的 Python 端口,它将不在前 127 个 ASCII 字符(“基本拉丁语”Unicode 块)中的字母、数字和符号 Unicode 字符(如果存在)转换为 ASCII 等效字符。
这是上述页面中的一个示例:
from fold_to_ascii import fold
s = u'Astroturf® paté'
fold(s)
> u'Astroturf pate'
fold(s, u'?')
> u'Astroturf? pate'
编辑:该fold_to_ascii
模块似乎适用于标准化拉丁字母;但是无法映射的字符被删除了,这意味着该模块将减少中文文本,例如,为空字符串。如果您想保留中文、日文和其他 Unicode 字母,请考虑使用remove_accent_chars_regex
上面的 @mo-han 实现。