Заменить не-ASCII-символы одним пространством

Мне нужно заменить все символы не-ASCII (\ x00-\x7F) пробелом. Я удивлен, что в Python это не так легко, если я ничего не пропустил. Следующая функция просто удаляет все символы, отличные от ASCII:

def remove_non_ascii_1(text):

    return ''.join(i for i in text if ord(i)<128)

И этот заменяет не-ASCII-символы количеством пробелов в соответствии с количеством байтов в кодовой точке символа (т.е. символ заменяется на 3 пробела):

def remove_non_ascii_2(text):

    return re.sub(r'[^\x00-\x7F]',' ', text)

Как заменить все символы, отличные от ASCII, на одно пространство?

myriad аналогичный qaru.site/info/40598/... questions, none адрес character замена как напротив , и дополнительно адресуйте все символы не-ascii, а не определенный символ.

Ответ 1

Ваше выражение ''.join() фильтрует, удаляет что-либо не-ASCII; вместо этого вы можете использовать условное выражение:

return ''.join([i if ord(i) < 128 else ' ' for i in text])

Это обрабатывает символы один за другим и все равно будет использовать одно место для замененного символа.

Ваше регулярное выражение должно просто заменить последовательные символы, отличные от ASCII, пробелом:

re.sub(r'[^\x00-\x7F]+',' ', text)

Обратите внимание на +.

Ответ 2

Для вас получить наиболее похожее представление вашей исходной строки. Я рекомендую модуль Unidecode:

from unidecode import unidecode
def remove_non_ascii(text):
    return unidecode(unicode(text, encoding = "utf-8"))

Затем вы можете использовать его в строке:

remove_non_ascii("Ceñía")
Cenia

Ответ 3

Для обработки символов используйте строки Unicode:

PythonWin 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32.
>>> s='ABC马克def'
>>> import re
>>> re.sub(r'[^\x00-\x7f]',r' ',s)   # Each char is a Unicode codepoint.
'ABC  def'
>>> b = s.encode('utf8')
>>> re.sub(rb'[^\x00-\x7f]',rb' ',b) # Each char is a 3-byte UTF-8 sequence.
b'ABC      def'

Но обратите внимание, что все равно будет проблема, если ваша строка содержит разложенные символы Unicode (например, отдельный символ и сочетание символов акцентов):

>>> s = 'mañana'
>>> len(s)
6
>>> import unicodedata as ud
>>> n=ud.normalize('NFD',s)
>>> n
'mañana'
>>> len(n)
7
>>> re.sub(r'[^\x00-\x7f]',r' ',s) # single codepoint
'ma ana'
>>> re.sub(r'[^\x00-\x7f]',r' ',n) # only combining mark replaced
'man ana'

Ответ 4

Если символ замены может быть?? вместо пробела, я бы предложил result = text.encode('ascii', 'replace').decode():

"""Test the performance of different non-ASCII replacement methods."""


import re
from timeit import timeit


# 10_000 is typical in the project that I'm working on and most of the text
# is going to be non-ASCII.
text = 'Æ' * 10_000


print(timeit(
    """
result = ''.join([c if ord(c) < 128 else '?' for c in text])
    """,
    number=1000,
    globals=globals(),
))

print(timeit(
    """
result = text.encode('ascii', 'replace').decode()
    """,
    number=1000,
    globals=globals(),
))

Результаты:

0.7208260721400134
0.009975979187503592

Ответ 5

Как насчет этого?

def replace_trash(unicode_string):
     for i in range(0, len(unicode_string)):
         try:
             unicode_string[i].encode("ascii")
         except:
              #means it non-ASCII
              unicode_string=unicode_string[i].replace(" ") #replacing it with a single space
     return unicode_string

Ответ 6

Как собственный и эффективный подход, вам не нужно использовать ord или любой цикл над символами. Просто кодируйте с ascii и игнорируйте ошибки.

Ниже будут просто удалены символы, отличные от ascii:

new_string = old_string.encode('ascii',errors='ignore')

Теперь, если вы хотите заменить удаленные символы, выполните следующие действия:

final_string = new_string + b' ' * (len(old_string) - len(new_string))

Ответ 7

Возможно, для другого вопроса, но я предоставляю свою версию ответа @Alvero (с использованием unidecode). Я хочу сделать "обычную" полосу для моих строк, то есть начало и конец моей строки для пробельных символов, а затем заменить только другие пробельные символы на "обычный" пробел, т.е.

"Ceñíaㅤmañanaㅤㅤㅤㅤ"

в

"Ceñía mañana"

,

def safely_stripped(s: str):
    return ' '.join(
        stripped for stripped in
        (bit.strip() for bit in
         ''.join((c if unidecode(c) else ' ') for c in s).strip().split())
        if stripped)

Сначала мы заменим все не-юникодные пробелы обычным пробелом (и снова включим его),

''.join((c if unidecode(c) else ' ') for c in s)

И затем мы разделяем это снова, с нормальным разделением Python, и удаляем каждый "бит",

(bit.strip() for bit in s.split())

И, наконец, присоединиться к ним снова, но только если строка проходит тест if,

' '.join(stripped for stripped in s if stripped)

И с этим, safely_stripped('ㅤㅤㅤㅤCeñíaㅤmañanaㅤㅤㅤㅤ') правильно возвращает 'Ceñía mañana'.