Как заставить интерпретатор Python правильно обрабатывать не-ASCII символы в строковых операциях? - PullRequest
100 голосов
/ 27 августа 2009

У меня есть строка, которая выглядит так:

6Â 918Â 417Â 712

Простой способ обрезать эту строку (как я понимаю Python) - просто сказать, что строка находится в переменной с именем s, мы получаем:

s.replace('Â ', '')

Это должно сработать. Но, конечно, он жалуется на то, что не-ASCII символ '\xc2' в файле blabla.py не закодирован.

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

Вот код, он действительно такой же, как и выше, но теперь он в контексте. Файл сохраняется как UTF-8 в блокноте и имеет следующий заголовок:

#!/usr/bin/python2.4
# -*- coding: utf-8 -*-

код:

f = urllib.urlopen(url)

soup = BeautifulSoup(f)

s = soup.find('div', {'id':'main_count'})

#making a print 's' here goes well. it shows 6Â 918Â 417Â 712

s.replace('Â ','')

save_main_count(s)

Это не дальше, чем s.replace ...

Ответы [ 11 ]

156 голосов
/ 27 августа 2009
def removeNonAscii(s): return "".join(filter(lambda x: ord(x)<128, s))

edit: мой первый импульс - всегда использовать фильтр, но выражение генератора более эффективно (и короче) использует память ...

def removeNonAscii(s): return "".join(i for i in s if ord(i)<128)

Имейте в виду, что это гарантированно работает с кодировкой UTF-8 (поскольку все байты в многобайтовых символах имеют старший бит, установленный в 1).

76 голосов
/ 27 августа 2009

Python 2 использует ascii в качестве кодировки по умолчанию для исходных файлов, что означает, что вы должны указать другую кодировку в верхней части файла, чтобы использовать не-ascii символы юникода в литералах. Python 3 использует utf-8 в качестве кодировки по умолчанию для исходных файлов, поэтому это не проблема.

См: http://docs.python.org/tutorial/interpreter.html#source-code-encoding

Чтобы включить кодировку источника utf-8, это должно быть в одной из двух верхних строк:

# -*- coding: utf-8 -*-

Выше приведено в документации, но это также работает:

# coding: utf-8

Дополнительные соображения:

  • Исходный файл также должен быть сохранен с использованием правильной кодировки в текстовом редакторе.

  • В Python 2 у литерала Юникода должен быть u перед ним, как в s.replace(u"Â ", u"") Но в Python 3 просто используйте кавычки. В Python 2 вы можете from __future__ import unicode_literals получить поведение Python 3, но имейте в виду, что это влияет на весь текущий модуль.

  • s.replace(u"Â ", u"") также завершится ошибкой, если s не является строкой Unicode.

  • string.replace возвращает новую строку и не редактирует на месте, поэтому убедитесь, что вы также используете возвращаемое значение

31 голосов
/ 27 августа 2009
>>> unicode_string = u"hello aåbäcö"
>>> unicode_string.encode("ascii", "ignore")
'hello abc'
16 голосов
/ 22 апреля 2012

Следующий код заменит все символы не ASCII на вопросительные знаки.

"".join([x if ord(x) < 128 else '?' for x in s])
6 голосов
/ 27 августа 2009

Использование Regex:

import re

strip_unicode = re.compile("([^-_a-zA-Z0-9!@#%&=,/'\";:~`\$\^\*\(\)\+\[\]\.\{\}\|\?\<\>\\]+|[^\s]+)")
print strip_unicode.sub('', u'6Â 918Â 417Â 712')
5 голосов
/ 08 ноября 2011

Слишком поздно для ответа, но исходная строка была в UTF-8, а '\ xc2 \ xa0' - это UTF-8 для NO-BREAK SPACE. Просто декодируйте исходную строку как s.decode('utf-8') (\ xa0 отображается как пробел при неправильном декодировании как Windows-1252 или latin-1:

Пример (Python 3)

s = b'6\xc2\xa0918\xc2\xa0417\xc2\xa0712'
print(s.decode('latin-1')) # incorrectly decoded
u = s.decode('utf8') # correctly decoded
print(u)
print(u.replace('\N{NO-BREAK SPACE}','_'))
print(u.replace('\xa0','-')) # \xa0 is Unicode for NO-BREAK SPACE

выход

6Â 918Â 417Â 712
6 918 417 712
6_918_417_712
6-918-417-712
3 голосов
/ 27 августа 2009
#!/usr/bin/env python
# -*- coding: utf-8 -*-

s = u"6Â 918Â 417Â 712"
s = s.replace(u"Â", "") 
print s

Это распечатает 6 918 417 712

2 голосов
/ 24 мая 2011

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

Использование : стр. перевод ( таблица [, deletechars] )

>>> trans_table = ''.join( [chr(i) for i in range(128)] + [' '] * 128 )

>>> 'Résultat'.translate(trans_table)
'R sultat'
>>> '6Â 918Â 417Â 712'.translate(trans_table)
'6  918  417  712'

Начиная с Python 2.6 , вы также можете установить для таблицы значение Нет и использовать deletechars для удаления ненужных символов, как в примерах, показанных в стандартных документах. в http://docs.python.org/library/stdtypes.html.

В случае строк Unicode таблица перевода представляет собой не строку из 256 символов, а диктовку с ord () соответствующих символов в качестве ключей. Но в любом случае получить правильную строку ascii из строки unicode достаточно просто, используя метод, упомянутый выше Truppo, а именно: unicode_string.encode ("ascii", "ignore")

В качестве резюме, если по какой-то причине вам абсолютно необходимо получить строку ascii (например, когда вы вызываете стандартное исключение с помощью raise Exception, ascii_message), вы можете использовать следующую функцию:

trans_table = ''.join( [chr(i) for i in range(128)] + ['?'] * 128 )
def ascii(s):
    if isinstance(s, unicode):
        return s.encode('ascii', 'replace')
    else:
        return s.translate(trans_table)

Хорошая вещь в переводе заключается в том, что вы можете конвертировать акцентированные символы в соответствующие неакцентированные символы ascii вместо того, чтобы просто удалять их или заменять на '?'. Это часто полезно, например, для целей индексации.

1 голос
/ 27 августа 2009

Это грязный хак, но может сработать.

s2 = ""
for i in s:
    if ord(i) < 128:
        s2 += i
1 голос
/ 27 августа 2009
s.replace(u'Â ', '')              # u before string is important

и создайте .py файл в кодировке Unicode.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...