简体   繁体   English

从 Python 中带重音的字符串中删除所有非字母字符

[英]Removing all non-letter chars from a string with accents in Python

I'm trying to delete all non-letter chars (except white-space) from a string containing accents using Python 3.7.我正在尝试使用 Python 3.7 从包含重音的字符串中删除所有非字母字符(空格除外)。 I tried the following:我尝试了以下方法:

import re

text = "Андре́й Серге́евич Арша́вин (род. 29 мая 1981[4], Ленинград) — российский футболист, бывший капитан сборной России, заслуженный мастер спорта России (2008)."
clean_text = re.sub('[\W_\d]+', ' ', text)
print(clean_text)

The output is输出是

Андре й Серге евич Арша вин род мая Ленинград российский футболист бывший капитан сборной России заслуженный мастер спорта России 

Why do I get a whitespace after the accented char in my result string?为什么我的结果字符串中的重音字符后面有一个空格? This seems to violate the principle of least surprise.这似乎违反了最小惊喜原则。 So I tried a different solution所以我尝试了不同的解决方案

text = "Андре́й Серге́евич Арша́вин (род. 29 мая 1981[4], Ленинград) — российский футболист, бывший капитан сборной России, заслуженный мастер спорта России (2008)."
clean_text2 = "".join(c for c in text if c.isalpha() or c == " ")
print(clean_text2)

The output is输出是

Андрей Сергеевич Аршавин род  мая  Ленинград  российский футболист бывший капитан сборной России заслуженный мастер спорта России 

This is nearly what I wanted, except that it removes the accents from the chars.这几乎是我想要的,只是它从字符中删除了重音。 I would like to have the following result:我想得到以下结果:

Андре́й Серге́евич Арша́вин род мая Ленинград российский футболист бывший капитан сборной России заслуженный мастер спорта России

Is there a way to remove all non-letter chars from a string, but keep the accents on the chars?有没有办法从字符串中删除所有非字母字符,但保留字符上的重音?

Basic solution for Russian word stress symbols俄语单词重音符号的基本解决方案

Russian letters do not have accents, the accent you have in the string shows the word stress, and is only used in specific written speech, like in textbooks for foreigners, books for children, etc.俄语字母没有重音,你在字符串中的重音表示单词重音,并且仅用于特定的书面语音,例如外国人教科书,儿童书籍等。

The е́ is a e letter and the char, 0301 COMBINING ACUTE ACCENT . е́是一个e字母和字符, 0301 COMBINING ACUTE ACCENT The only accent diacritic can be subtracted from your pattern to get the results you want:可以从您的模式中减去唯一的重音变音符号以获得您想要的结果:

clean_text = re.sub(r'(?:(?!\u0301)[\W\d_])+', ' ', text)

See the Python demo yielding参见Python 演示产生

Андре́й Серге́евич Арша́вин род мая Ленинград российский футболист бывший капитан сборной России заслуженный мастер спорта России

See the regex demo online .在线查看正则表达式演示

Solution supporting all diacritics - PyPi regex module支持所有变音符号的解决方案 - PyPi 正则表达式模块

To keep all diacritic marks, the easiest is to install PyPi regex module (with pip install regex ) and then use \\p{L} and \\p{M} Unicode property classes:要保留所有变音符号,最简单的方法是安装PyPi regex 模块(使用pip install regex ),然后使用\\p{L}\\p{M} Unicode 属性类:

import regex

text = "Андре́й Серге́евич Арша́вин (род. 29 мая 1981[4], Ленинград) — российский футболист, бывший капитан сборной России, заслуженный мастер спорта России (2008)."
print ( regex.sub(r'[^\p{L}\p{M}]+', ' ', text) )
# => Андре́й Серге́евич Арша́вин род мая Ленинград российский футболист бывший капитан сборной России заслуженный мастер спорта России
print( " ".join(regex.findall(r'(?>\p{L}\p{M}*+)+', text)) ) 
# => Андре́й Серге́евич Арша́вин род мая Ленинград российский футболист бывший капитан сборной России заслуженный мастер спорта России

Here, \\[^\\p{L}\\p{M}\\]+ regex matches any 1 or more chars other than Unicode letters ( \\p{L} ) and diacritic characters ( \\p{M} ).这里, \\[^\\p{L}\\p{M}\\]+正则表达式匹配除 Unicode 字母 ( \\p{L} ) 和变音符号 ( \\p{M} ) 之外的任何 1 个或多个字符。 The other solution, (?>\\p{L}\\p{M}*+)+ with re.findall , extracts all letter + diacritic chunks from the text and then " ".join(...) concats them with a space.另一个解决方案, (?>\\p{L}\\p{M}*+)+re.findall ,从文本中提取所有字母 + 变音符号块,然后" ".join(...)空间。

Diacritics support with Python re Python re变音符号支持

You will need to "spell out" the \\p{M} class and you may match any Unicode letter using [^\\W\\d_] construct.您需要“拼写” \\p{M}类,并且可以使用[^\\W\\d_]构造匹配任何 Unicode 字母。 It makes sense to use the find-all-words-and-then-concatenate approach here rather than re.sub :这里使用find-all-words-and-then-concatenate方法而不是re.sub是有意义的:

import re
combining_marks_bmp = '\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F'
combining_marks_astral = '\uD805[\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7\uDF1D-\uDF2B]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD82F[\uDC9D\uDC9E]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD83A[\uDCD0-\uDCD6]|\uDB40[\uDD00-\uDDEF]'
letter = r'[^\W\d_]'
pat = re.compile(r'(?:{}|[{}]|{})+'.format(letter,combining_marks_bmp, combining_marks_astral))
print(" ".join(pat.findall(text)))
# => Андре́й Серге́евич Арша́вин род мая Ленинград российский футболист бывший капитан сборной России заслуженный мастер спорта России

See the online Python demo查看在线 Python 演示

Try (?:[^\\w\\x{301}\\s]|[\\d_])+试试(?:[^\\w\\x{301}\\s]|[\\d_])+
Use \́ instead of \\x{301} if it uses that notation如果使用该符号,请使用 \́ 而不是 \\x{301}

Or use properties if supported或者使用属性(如果支持)

[^\\p{L}\\x{0301}\\s]+

I want to offer a Pythonic solution that does not involve regular expressions.我想提供一个不涉及正则表达式的 Pythonic 解决方案。

It uses the translate method on strings.它对字符串使用translate方法。

1. Python documenation on str.maketrans 1. str.maketrans 上的 Python str.maketrans

2. Python documentation on str.translate 2. 关于str.translate Python 文档

from string import digits
import itertools as it
import unicodedata

# This creates a special dictionary to pass to the translation method.
# This will replace all digits and punctuation with an empty string

translation = str.maketrans(
    dict(
        zip(
            (
                *digits,
                *( # punctuation
                    item
                    for item in set(text)
                    if unicodedata.category(item).startswith("P")
                ),
            ),
            it.cycle(("",)),
        )
    )
)

print(" ".join(text.translate(translation).split()))

OUTPUT:输出:

Андре́й Серге́евич Арша́вин род мая Ленинград российский футболист бывший капитан сборной России заслуженный мастер спорта России

You can choose any character for the substitution.您可以选择任何字符进行替换。 I chose an empty string "" for deletion.我选择了一个空字符串""进行删除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM