Как заставить интерпретатор Python правильно обрабатывать символы, отличные от ASCII, в строковых операциях?

104

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

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...

Adergaard
источник
1
Пробовал все 4 ответа. Нет. Все еще получаю UnicodeDecodeError: кодек ascii не может декодировать байт 0xc2 в позиции 1: порядковый номер не в диапазоне (128)
адергаард
перед вашей строкой Unicode должна стоятьu
SilentGhost
@SilentGhost: как видите, нельзя быть уверенным, что это строка в Юникоде. Я получаю строку, содержащую показанное выше содержимое, но содержащую строки, отличные от ascii. Это настоящая проблема. Я предполагаю, что это юникод, так как его нет в первых 128.
адергаард
Ошибка не имеет ничего общего с входящей строкой. Это строка в вашем коде, которая вызывает эту ошибку!
SilentGhost
2
Готов поспорить, именно поэтому Python 3 так строго относится к различию между строками и последовательностями байтов, просто чтобы избежать такой путаницы.
Марк Рэнсом,

Ответы:

84

Python 2 использует asciiкодировку по умолчанию для исходных файлов, что означает, что вы должны указать другую кодировку в верхней части файла, чтобы использовать символы Unicode, отличные от 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 литерал Unicode должен иметь uперед ним, как и s.replace(u"Â ", u"")в Python 3, просто используйте кавычки. В Python 2 вы можете from __future__ import unicode_literalsполучить поведение Python 3, но имейте в виду, что это влияет на весь текущий модуль.

  • s.replace(u"Â ", u"")также не удастся, если sэто не строка в Юникоде.

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

Джейсон С
источник
4
На самом деле вам только нужно # coding: utf-8. -*-не для украшения, но вряд ли вам когда-нибудь понадобится. Думаю, это было для старых снарядов.
fmalina 09
157
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).

Фортран
источник
1
Я получаю: TypeError: ord () ожидал символ, но найдена строка длиной 2
Ивелин
@Ivelin, потому что «символ» не интерпретируется как правильный юникод ... проверьте, что ваша исходная строка имеет префикс, uесли это литерал.
Фортран
35
>>> unicode_string = u"hello aåbäcö"
>>> unicode_string.encode("ascii", "ignore")
'hello abc'
труппо
источник
4
Я вижу, сколько голосов вы получили, но когда я пробую, он говорит: Нет. UnicodeDecodeError: кодек ascii не может декодировать байт 0xc2 в позиции 1: порядковый номер не в диапазоне (128). Может быть, моя исходная строка не в юникоде? Хорошо в любом случае. это нужно
адергаард
2
Хорошо, спасибо. Могу я предложить использовать .decode () для результата, чтобы получить его в исходной кодировке?
AkiRoss
Если вы получаете UnicodeDecodeError: 'ascii', попробуйте преобразовать строку в формат «UTF-8» перед применением функции кодирования.
Сатиш
16

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

"".join([x if ord(x) < 128 else '?' for x in s])
VisioN
источник
Из любопытства я хотел узнать, есть ли какая-то конкретная причина заменить его знаком вопроса?
Mohsin
6

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

import re

strip_unicode = re.compile("([^-_a-zA-Z0-9!@#%&=,/'\";:~`\$\^\*\(\)\+\[\]\.\{\}\|\?\<\>\\]+|[^\s]+)")
print strip_unicode.sub('', u'6Â 918Â 417Â 712')
Акой Мекс
источник
5

Слишком поздно для ответа, но исходная строка была в 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

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

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

Исайя
источник
Нет. UnicodeDecodeError: кодек ascii не может декодировать байт 0xc2 в позиции 1: порядковый номер не в диапазоне (128). Может быть, моя исходная строка не в юникоде? Хорошо в любом случае. Я наверное что-то не так делаю.
Adergaard
@adergaard, вы добавили # - - coding: utf-8 - - вверху исходного файла?
Надя Алрамли
Да, посмотрите снова вверху этой страницы, я отредактировал questoin и вставил код и комментарии заголовка. Спасибо за вашу помощь.
Adergaard
Я думаю, вам придется разобраться, как получить строки из документа html или xml в юникоде. Подробнее об этом здесь: diveintopython.org/xml_processing/unicode.html
Исайя
2

Я знаю, что это старый поток, но я чувствовал себя обязанным упомянуть метод 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 , вы также можете установить для таблицы значение None и использовать deletechars для удаления ненужных символов, как в примерах, показанных в стандартных документах по адресу http://docs.python.org/library/stdtypes. html .

В строках Unicode таблица перевода представляет собой не 256-символьную строку, а dict с 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)

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

Louis LC
источник
Я получаю: TypeError: отображение символов должно возвращать целое число, None или unicode
Ivelin
1
s.replace(u'Â ', '')              # u before string is important

и сделайте свой .pyфайл unicode.

Тихий призрак
источник
1

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

s2 = ""
for i in s:
    if ord(i) < 128:
        s2 += i
Кори Д.
источник
0

Как бы то ни было, у меня был набор символов, utf-8и я включил классическую # -*- coding: utf-8 -*-строчку " ".

Однако при чтении этих данных с веб-страницы я обнаружил, что у меня не было универсальных новых строк.

В моем тексте было два слова, разделенных знаком « \r\n». Я только разрезал \nи заменил "\n".

Как только я просмотрел и увидел рассматриваемый набор символов, я понял ошибку.

Таким образом, он также может быть в наборе символов ASCII , но это символ, которого вы не ожидали.

Glen
источник