【问题标题】:Remove accents and keep under dots in Python在 Python 中删除重音符号并保留在点下
【发布时间】:2019-12-18 14:15:38
【问题描述】:

我正在处理一项 NLP 任务,该任务需要使用称为约鲁巴语的语料库。约鲁巴语是一种在字母表中有变音符号(重音)和点下方的语言。例如,这是一个约鲁巴语字符串:"ọmọàbúròẹlẹ́wà",我需要删除重音符号并保留下面的点。

我曾尝试在 Python 中使用 unidecode 库,但它会删除重音符号和点下方。

import unidecode
ac_stng = "ọmọàbúròẹlẹ́wà"
unac_stng = unidecode.unidecode(ac_stng)

我希望输出为"ọmọaburoẹlẹwa"。但是,当我在 Python 中使用unidecode 库时,我得到了"omoaburoelewa"

【问题讨论】:

  • 您可能需要自己编写该逻辑,否则不同类型的变音符号之间没有区别。

标签: python python-3.x string nlp python-unicode


【解决方案1】:

我会为此使用Unicode normalization

带有类似重音符号和圆点的字符是precomposed Unicode characters。如果你分解它们,你可以获得基本字符加上combining characters 用于重音和点等等。然后你可以删除你不想要的并将字符串重新组合成预先组合的字符。

您可以在 Python 中使用 unicodedata.normalize 执行此操作。具体来说,您需要“NFD”(规范化形式规范分解)规范化形式。这将为您提供字符的规范分解。然后要重新组合字符,您需要“NFC”(规范化形式规范组合)。

我会告诉你我的意思。首先,让我们看一下您上面提供的示例文本的各个代码点:

>>> from pprint import pprint
>>> import unicodedata
>>> text = 'ọmọàbúròẹlẹ́wà'
>>> pprint([unicodedata.name(c) for c in text])
['LATIN SMALL LETTER O WITH DOT BELOW',
 'LATIN SMALL LETTER M',
 'LATIN SMALL LETTER O WITH DOT BELOW',
 'LATIN SMALL LETTER A WITH GRAVE',
 'LATIN SMALL LETTER B',
 'LATIN SMALL LETTER U WITH ACUTE',
 'LATIN SMALL LETTER R',
 'LATIN SMALL LETTER O WITH GRAVE',
 'LATIN SMALL LETTER E WITH DOT BELOW',
 'LATIN SMALL LETTER L',
 'LATIN SMALL LETTER E WITH ACUTE',
 'COMBINING DOT BELOW',
 'LATIN SMALL LETTER W',
 'LATIN SMALL LETTER A WITH GRAVE']

如您所见,其中一个字符已经部分分解(带有单独的“COMBINING DOT BELOW”的字符)。现在让我们看看它完全分解:

>>> text = unicodedata.normalize('NFD', text)
>>> pprint([unicodedata.name(c) for c in text])
['LATIN SMALL LETTER O',
 'COMBINING DOT BELOW',
 'LATIN SMALL LETTER M',
 'LATIN SMALL LETTER O',
 'COMBINING DOT BELOW',
 'LATIN SMALL LETTER A',
 'COMBINING GRAVE ACCENT',
 'LATIN SMALL LETTER B',
 'LATIN SMALL LETTER U',
 'COMBINING ACUTE ACCENT',
 'LATIN SMALL LETTER R',
 'LATIN SMALL LETTER O',
 'COMBINING GRAVE ACCENT',
 'LATIN SMALL LETTER E',
 'COMBINING DOT BELOW',
 'LATIN SMALL LETTER L',
 'LATIN SMALL LETTER E',
 'COMBINING DOT BELOW',
 'COMBINING ACUTE ACCENT',
 'LATIN SMALL LETTER W',
 'LATIN SMALL LETTER A',
 'COMBINING GRAVE ACCENT']

现在根据您的要求,听起来您想保留所有拉丁字母(我猜可能还有 ASCII 的其余部分)加上“COMBINING DOT BELOW”代码点,我们可以参考使用文字 '\N{COMBINING DOT BELOW}' 让您的代码更易于阅读。

这是一个示例函数,我认为它可以满足您的需求:

import unicodedata

def remove_accents_but_not_dots(input_text):
    # Step 1: Decompose input_text into base letters and combinining characters
    decomposed_text = unicodedata.normalize('NFD', input_text)

    # Step 2: Filter out the combining characters we don't want
    filtered_text = ''
    for c in decomposed_text:
        if ord(c) <= 0x7f or c == '\N{COMBINING DOT BELOW}':
            # Only keep ASCII or "COMBINING DOT BELOW"
            filtered_text += c

    # Step 3: Re-compose the string into precomposed characters
    return unicodedata.normalize('NFC', filtered_text)

(当然,Python 中的字符串连接很慢,但我会把优化留给你。这个例子是为了可读性而写的。)

结果如下:

>>> remove_accents_but_not_dots('ọmọàbúròẹlẹ́wà')
'ọmọaburoẹlẹwa'

【讨论】:

    【解决方案2】:

    由于您要执行特定类型的重音解析,因此自己编写解析器可能是最简单的。本质上,您可以使用ord() 检查字符串中每个字母的 unicode 值,并根据 unicode 值列表检查是否有不受欢迎的重音字母。有两个步骤,我看到它的方式:

    首先是处理只有变音符号,没有点的字符。从我对这种语言的admittedly cursory research 看来,对于给定的元音,它似乎有三个可能的变音符号;急性、严重和长音。然后,对于给定的元音,您可以创建一个包含每个变音符号变体的 unicode 数字的数组。因此,对于字母“a”,您将拥有以下内容:

    a_diacritics = [224, 225, 257] # Unicode values for á, à, and ā
    

    然后您可以将输入中每个字母的 unicode 值与该数组进行比较,如果匹配,则将其与普通的“a”交换:

    input_string = "ọmọàbúròẹlẹ́wà"
    output = ""
    for letter in input:
        if ord(letter) in a_diacritics:
            output += 'a'
        else:
            output += letter
    

    运行那段代码后,变量output 将等于"ọmọabúròẹlẹ́wa"。然后,您将使用 unicode values 为其他元音编写类似的数组和解析逻辑。

    第二部分是带有变音符号和点的字符。像“ẹ́”这样的字母在技术上通常是两个独立的字符。在“ẹ́”的情况下,它是“é”和'combining dot below' 字符,但是在视觉上相同的“ẹ́”的情况下,它是“ẹ”和'combining acute accent' 字符。 对于添加了点字符的字母,前面的数组步骤会处理它们。然后,对于添加的变音符号,您可以为它们的 unicode 值创建一个最终数组:

    diacritic_marks = [769, 768, 772] # Unicode values for acute, grave, and macron diacritics
    

    然后让解析循环忽略这些字符:

    for letter in input_string:
        if ord(letter) in a_diacritics:
            output += 'a'
        elif ord(letter) in diacritic_marks:
            pass
        else:
            output += letter
    

    【讨论】:

      猜你喜欢
      • 2016-06-26
      • 2018-07-18
      • 2014-03-16
      • 2014-06-20
      • 2017-07-06
      • 2022-01-03
      • 1970-01-01
      相关资源
      最近更新 更多