Як змусити інтерпретатора 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...


1
Спробував усі 4 відповіді поки що. Не йдіть. Все ще отримуючи UnicodeDecodeError: 'ascii' кодек не може розшифрувати байт 0xc2 в позиції 1: порядковий не в діапазоні (128)
adergaard

ваш Unicode рядок повинен бути попередньоu
SilentGhost

@SilentGhost: як ви бачите, немає можливості бути впевненим, що це рядок Unicode. Я отримую рядок із вмістом, показаним вище, але він містить рядки non ascii. У цьому справжня проблема. Я здогадуюсь, що це unicode, оскільки це не в першій 128.
adergaard

Помилка не має нічого спільного з вхідним рядком. Ця рядок у вашому коді викликає цю помилку!
SilentGhost

2
Б'юсь об заклад, саме тому Python 3 настільки суворо ставиться до різниці між рядками та послідовностями байтів, просто щоб уникнути подібної плутанини.
Марк Рансом

Відповіді:


84

Python 2 використовує asciiкодування за замовчуванням для вихідних файлів, а це означає, що ви повинні вказати інше кодування у верхній частині файлу, щоб використовувати символи unicode unicode у літералах. 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це не рядок unicode.

  • string.replace повертає новий рядок і не редагується на місці, тому переконайтеся, що ви також використовуєте значення повернення


4
Вам насправді потрібно лише # coding: utf-8. -*-не для прикраси, але ви навряд чи коли-небудь знадобляться. Я думаю, це було там для старих снарядів.
fmalina

157
def removeNonAscii(s): return "".join(filter(lambda x: ord(x)<128, s))

редагувати: мій перший імпульс - завжди використовувати фільтр, але вираз генератора - більш ефективний (і коротший) об'єм пам'яті ...

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

Майте на увазі, що це гарантовано для роботи з кодуванням UTF-8 (адже всі байти в багатобайтових символах мають найвищий біт, встановлений на 1).


1
Я отримую: TypeError: ord () очікував символу, але знайдено рядок довжиною 2
Ivelin

@Ivelin це тому, що "символ" не інтерпретується як належний unicode ... перевірте, чи є ваш вихідний рядок з префіксом, uякщо він є буквальним.
фортран

35
>>> unicode_string = u"hello aåbäcö"
>>> unicode_string.encode("ascii", "ignore")
'hello abc'

4
Я бачу голоси, які ви отримуєте, але коли я намагаюся, це говорить: Ні. UnicodeDecodeError: кодек "ascii" не може декодувати байт 0xc2 в позиції 1: порядковий не знаходиться в діапазоні (128). Може бути так, що моя оригінальна рядок не знаходиться в unicode? Ну в будь-якому випадку. це потрібно
adergaard

2
Приємно, дякую. Чи можу я запропонувати використовувати результат .decode (), щоб отримати його у вихідному кодуванні?
AkiRoss

Якщо ви отримуєте UnicodeDecodeError: 'ascii', спробуйте перетворити рядок у формат '' UTF-8 'перед застосуванням функції кодування.
Саетеш

16

Наступний код замінить усі символи ASCII не знаками запитання.

"".join([x if ord(x) < 128 else '?' for x in s])

З цікавості я хотів це знати, чи є якась конкретна причина замінити його знаком питання?
Мохін

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). Може бути так, що моя оригінальна рядок не знаходиться в unicode? Ну в будь-якому випадку. Я, мабуть, щось неправильно роблю.
adergaard

@adergaard, ти додав # - - кодування: utf-8 - - у верхній частині вихідного файлу?
Надія Алрамлі

Так, ще раз перегляньте верхню частину сторінки, я відредагував черга та ввів код та коментарі до заголовка. Дякуємо за вашу допомогу.
adergaard

Я думаю, вам доведеться розібратися, як отримати рядки з html або xml документа в unicode. Більше інформації про це тут: diveintopython.org/xml_processing/unicode.html
Ісая

2

Я знаю, що це стара нитка, але я змушений згадати метод перекладу, який завжди є хорошим способом замінити всі коди символів вище 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-символьний рядок, а дікта з порядком () відповідних символів як ключів. Але в будь-якому випадку отримати належну рядок 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, які не мають акценту, замість того, щоб просто видалити їх або замінити на "?". Це часто корисно, наприклад, для індексування.


Я отримую: 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 -*-лінію " ".

Однак я виявив, що не мав Universal Newlines, читаючи ці дані з веб-сторінки.

У моєму тексті було два слова, розділені " \r\n". Я тільки розколював на \nта замінював "\n".

Одного разу я переглянув і побачив набір персонажів, про які йдеться, я зрозумів помилку.

Отже, це може бути і в наборі символів ASCII , але символу, якого ви не очікували.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.