UnicodeDecodeError: 'utf8' кодек не може розшифрувати байт 0x9c


289

У мене є сервер сокет, який повинен приймати дійсні символи UTF-8 від клієнтів.

Проблема полягає в тому, що деякі клієнти (в основному хакери) надсилають через нього всі неправильні види даних.

Я можу легко відрізнити справжнього клієнта, але я реєструю у файлах усі надіслані дані, щоб потім проаналізувати його.

Іноді я отримую таких персонажів, œякі викликають UnicodeDecodeErrorпомилку.

Мені потрібно вміти робити рядок UTF-8 з цими символами або без них.


Оновлення:

У моєму конкретному випадку послуга сокета була MTA, і тому я очікую лише отримання команд ASCII, таких як:

EHLO example.com
MAIL FROM: <john.doe@example.com>
...

Я все це записував у JSON.

Потім деякі люди без добрих намірів вирішили продати всілякі барахли.

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


1
виходить рядок з файлу чи сокета? Чи можете ви, будь ласка, розмістити приклади коду того, як рядок кодується в кінці кінця, розшифровується, перш ніж він буде надісланий через socket / filehandler?
devsnd

Я писав чи не писав, що рядок надходить через сокет? Я просто прочитав рядок з сокета і з, щоб помістити його у словник, а потім JSON - щоб надіслати його разом. Не вдалося виконати функцію JSON через ці символи.
трансіллад

чи можете ви, будь ласка, вкладіть свої зразкові дані проблеми
Shubham Sharma

Відповіді:


343

http://docs.python.org/howto/unicode.html#the-unicode-type

str = unicode(str, errors='replace')

або

str = unicode(str, errors='ignore')

Примітка. Це усуне (проігнорує) символи, про які йдеться, повертаючи рядок без них.

Для мене це ідеальний випадок, оскільки я використовую його як захист від входу, який не є ASCII, що заборонено моєю програмою.

Як варіант: Використовуйте відкритий метод з codecsмодуля для читання у файлі:

import codecs
with codecs.open(file_name, 'r', encoding='utf-8',
                 errors='ignore') as fdata:

45
Так, хоча це зазвичай погана практика / небезпечна, тому що ви просто втратите персонажів. Краще визначити або виявити кодування вхідного рядка та розшифрувати його спершу для однокодування, а потім кодувати як UTF-8, наприклад:str.decode('cp1252').encode('utf-8')
Бен Хойт

У деяких випадках так, ви праві, це може спричинити проблеми. У моєму випадку я не переймаюся ними, оскільки вони здаються зайвими символами, що виникають із-за поганого форматування та програмування клієнтів, що підключаються до мого серверного сокета.
transilvlad

Цей фактично допомагає, якщо вміст рядка насправді недійсний, у моєму випадку, '\xc0msterdam'який переходить на u'\ufffdmsterdam'заміну
PvdL

3
якщо ви опинилися тут, тому що у вас виникають проблеми з читанням файлу, відкриття файлу у бінарному режимі може допомогти: open(file_name, "rb")а потім застосувати підхід Бена з коментарів вище
Крістіан

той же варіант стосується ще більше, наприклад, до "something.decode ()"
Олександр Стор

83

Зміна двигуна з C на Python зробила для мене трюк.

Двигун C:

pd.read_csv(gdp_path, sep='\t', engine='c')

"utf-8" кодек не може розшифрувати байт 0x92 у позиції 18: недійсний початковий байт

Двигун - Python:

pd.read_csv(gdp_path, sep='\t', engine='python')

Немає помилок для мене.


3
це насправді гарне рішення. я не знаю, чому це було знято.
ℕʘʘḆḽḘ

Це може бути не дуже хорошою ідеєю, якщо у вас є величезний csvфайл. Це може призвести до OutOfMemoryпомилки або автоматичного перезавантаження ядра ноутбука. Ви повинні встановити encodingцей випадок.
LucasBr

1
Відмінна відповідь. Спасибі. Це працювало для мене. Я мав "?" Всередині ромбовидного персонажа, який викликав проблему. З простими очима у мене був "" "дюйм. Я зробив 2 речі, щоб розібратися. a) df = pd.read_csv ('test.csv', n_rows = 10000). Це прекрасно спрацювало без двигуна. Тож я збільшив n_rows, щоб зрозуміти, у якому рядку була помилка. б) df = pd.read_csv ('test.csv', engine = 'python'). Це спрацювало, і я надрукував помилковий рядок за допомогою df.iloc [36145], він надрукував мені помилковий запис.
Джаганнат Банерджі

1
це теж працювало для мене ... Не впевнений, що відбувається "під кришкою", і якщо це насправді гарне / добре / правильне рішення у всіх випадках, але це зробило для мене хитрість;)
Chrisvdberge

1
Чудове рішення! Дуже дякую.
Печі

62

Цей тип випуску урожаю для мене тепер, коли я перейшов на Python 3. Я поняття не мав, що Python 2 просто прокручує будь-які проблеми з кодуванням файлів.

Я знайшов це приємне пояснення відмінностей і як знайти рішення після того, як жодне з перерахованого вище не працювало для мене.

http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html

Коротше кажучи, щоб змусити Python 3 поводитись максимально схоже на використання Python 2:

with open(filename, encoding="latin-1") as datafile:
    # work on datafile here

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


29
>>> '\x9c'.decode('cp1252')
u'\u0153'
>>> print '\x9c'.decode('cp1252')
œ

16
Я розгублений, як ти вибрав cp1252? Це працювало на мене, але чому? Я не знаю, і тепер я загубився: /. Не могли б ви детальніше? Дуже дякую ! :)
Кирило Н.

4
Чи можете ви представити варіант, який працює для всіх персонажів? Чи є спосіб виявити символи, які потрібно розшифрувати, щоб можна було реалізувати більш загальний код? Я бачу, що багато людей дивляться на це, і я ставлю на облік, що деякі відмови не є бажаним варіантом, як це для мене.
transilvlad

Як бачимо, це питання має досить велику популярність. Думаєте, ви могли б розширити свою відповідь більш загальним рішенням?
transilvlad

13
Немає більш загального рішення "Вгадай рулетку кодування"
Щеня

5
знайшли це, використовуючи поєднання веб-пошуку, удачі та інтуїції: cp1252 wasused by default in the legacy components of Microsoft Windows in English and some other Western languages
bolov

24

У мене була UnicodeDecodeErrorтака ж проблема, і я вирішив її за допомогою цієї лінії. Не знаю, чи це найкращий спосіб, але це працювало для мене.

str = str.decode('unicode_escape').encode('utf-8')

13

Перше, використовуючи get_encoding_type для отримання типу файлів коду:

import os    
from chardet import detect

# get file encoding type
def get_encoding_type(file):
    with open(file, 'rb') as f:
        rawdata = f.read()
    return detect(rawdata)['encoding']

друге, відкриття файлів типу:

open(current_file, 'r', encoding = get_encoding_type, errors='ignore')

1
що станеться, коли воно повернеться Нічого
Чоп Лабалагун

3

Про всяк випадок, якщо хтось має таку ж проблему. Я використовую vim з YouCompleteMe , не вдалося запустити ycmd з цим повідомленням про помилку, що я зробив: це export LC_CTYPE="en_US.UTF-8", проблема зникла.


2
Як це стосується цього питання?
transilvlad

1
Точно так само, якщо ви знаєте, як працює ваше завершення. Плагін Ycm - це архітектура сокета, зв’язок між клієнтом і сервером використовується сокетом, обидва - це модулі python, не в змозі розшифрувати пакети, якщо параметр кодування невірний
workplaylifecycle

У мене така ж проблема. Скажіть, будь ласка, куди подіти export LC_CTYPE="en_US.UTF-8"?
Реман

@Remonn привіт, ти знаєш, у нас є файл профілю для bash? Покладіть всередину.
workplaylifecycle

@hylepo, я в системі Windows :)
Реман

3

Що ви можете зробити, якщо вам потрібно внести зміни у файл, але не знаєте кодування файлу? Якщо ви знаєте, що кодування сумісне з ASCII і хочете лише вивчити або змінити частини ASCII, ви можете відкрити файл за допомогою обробника помилок сурогатного пейзажу:

with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
    data = f.read()

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