Эффективно заменить плохие символы

Я часто работаю с текстом utf-8, содержащим символы вроде:

\ xc2\x99

\ xc2\x95

\ xc2\x85

и т.д.

Эти символы путают другие библиотеки, с которыми я работаю, поэтому их нужно заменить.

Каков эффективный способ сделать это, а не:

text.replace('\xc2\x99', ' ').replace('\xc2\x85, '...')

Ответ 1

Всегда есть регулярные выражения; просто перечислите всех нарушителей в квадратных скобках так:

import re
print re.sub(r'[\xc2\x99]'," ","Hello\xc2There\x99")

Это печатает: "Hello There", при этом ненужные символы заменяются пробелами.

Альтернативно, если у вас есть другой символ замены для каждого:

# remove annoying characters
chars = {
    '\xc2\x82' : ',',        # High code comma
    '\xc2\x84' : ',,',       # High code double comma
    '\xc2\x85' : '...',      # Tripple dot
    '\xc2\x88' : '^',        # High carat
    '\xc2\x91' : '\x27',     # Forward single quote
    '\xc2\x92' : '\x27',     # Reverse single quote
    '\xc2\x93' : '\x22',     # Forward double quote
    '\xc2\x94' : '\x22',     # Reverse double quote
    '\xc2\x95' : ' ',
    '\xc2\x96' : '-',        # High hyphen
    '\xc2\x97' : '--',       # Double hyphen
    '\xc2\x99' : ' ',
    '\xc2\xa0' : ' ',
    '\xc2\xa6' : '|',        # Split vertical bar
    '\xc2\xab' : '<<',       # Double less than
    '\xc2\xbb' : '>>',       # Double greater than
    '\xc2\xbc' : '1/4',      # one quarter
    '\xc2\xbd' : '1/2',      # one half
    '\xc2\xbe' : '3/4',      # three quarters
    '\xca\xbf' : '\x27',     # c-single quote
    '\xcc\xa8' : '',         # modifier - under curve
    '\xcc\xb1' : ''          # modifier - under line
}
def replace_chars(match):
    char = match.group(0)
    return chars[char]
return re.sub('(' + '|'.join(chars.keys()) + ')', replace_chars, text)

Ответ 2

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

\xc2\x95 - это кодировка UTF-8 символа U + 0095, которая является управляющим символом C1 (MESSAGE WAITING). Неудивительно, что ваша библиотека не может справиться с этим. Но вопрос в том, как он попал в ваши данные?

Ну, возможно, очень вероятно, что он начинался с того, что символ 0x95 (BULLET) в кодировке Windows-1252 был неправильно декодирован как U + 0095 вместо правильного U + 2022, а затем закодирован в UTF-8. (Японский термин mojibake описывает эту ошибку.)

Если это правильно, вы можете восстановить исходные символы, вернув их обратно в Windows-1252, а затем на этот раз правильно декодировать их в Unicode. (В этих примерах я использую Python 3.3, эти операции немного отличаются в Python 2.)

>>> b'\x95'.decode('windows-1252')
'\u2022'
>>> import unicodedata
>>> unicodedata.name(_)
'BULLET'

Если вы хотите сделать эту коррекцию для всех символов в диапазоне 0x80-0x99, которые являются действительными символами Windows-1252, вы можете использовать этот подход:

def restore_windows_1252_characters(s):
    """Replace C1 control characters in the Unicode string s by the
    characters at the corresponding code points in Windows-1252,
    where possible.

    """
    import re
    def to_windows_1252(match):
        try:
            return bytes([ord(match.group(0))]).decode('windows-1252')
        except UnicodeDecodeError:
            # No character at the corresponding code point: remove it.
            return ''
    return re.sub(r'[\u0080-\u0099]', to_windows_1252, s)

Например:

>>> restore_windows_1252_characters('\x95\x99\x85')
'•™…'

Ответ 3

Если вы хотите удалить все символы, отличные от ASCII, из строки, вы можете использовать

text.encode("ascii", "ignore")

Ответ 4

import unicodedata

# Convert to unicode
text_to_uncicode = unicode(text, "utf-8")           

# Convert back to ascii
text_fixed = unicodedata.normalize('NFKD',text_to_unicode).encode('ascii','ignore')         

Ответ 5

Это не "символы Unicode" - это скорее похоже на кодированную строку UTF-8. (Хотя ваш префикс должен быть \xC3, а не \xC2 для большинства символов). Вы не должны просто выбросить их в 95% случаев, если только вы не общаетесь с бэкэндом COBOL. Знаете, мир не ограничен 26 символами.

Существует краткое описание объяснения различий между строками Unicode (что используется как объект Unicode в python 2 и как строки в Python 3 здесь: http://www.joelonsoftware.com/articles/Unicode.html - пожалуйста, ради вашего читайте это. Даже если вы никогда не планируете иметь что-либо, что не является английским во всех ваших приложениях, вы все равно будете натыкаться на символы типа € или º, которые не подходят в 7 бит ASCII. Эта статья поможет вам.

Тем не менее, возможно, библиотеки, которые вы используете, принимают юникодные объекты python, и вы можете преобразовать строки UTF-8 Python 2 в unidoce, выполнив:

var_unicode = var.decode("utf-8")

Если вам действительно нужен 100% чистый ASCII, заменив все символы без ASCII, после декодирования строки в unicode, перекодируйте ее в ASCII, указав ей игнорировать символы, которые не вписываются в кодировку, с помощью:

var_ascii = var_unicode.encode("ascii", "replace")

Ответ 6

Эти символы не находятся в ASCII Library, и именно поэтому вы получаете ошибки. Чтобы избежать этих ошибок, во время чтения файла вы можете сделать следующее.

import codecs   
f = codecs.open('file.txt', 'r',encoding='utf-8')

Чтобы узнать больше об этих ошибках, просмотрите эту ссылку.