В двоичном формате 0xE9 выглядит как 1110 1001. Если вы прочитаете о UTF-8 в Википедии, вы увидите, что за таким байтом должны следовать два вида 10xx xxxx. Так, например:
>>> b'\xe9\x80\x80'.decode('utf-8') u'\u9000'
Но это всего лишь механическая причина исключения. В этом случае у вас есть строка, которая почти наверняка закодирована в latin 1. Вы можете видеть, насколько UTF-8 и latin 1 выглядят по-разному:
(Обратите внимание, здесь я использую сочетание представления Python 2 и 3. Входные данные допустимы в любой версии Python, но ваш интерпретатор Python вряд ли действительно будет отображать как юникод, так и байтовые строки таким образом.)
Ответ 3
Недопустимый UTF-8. Этот символ является символом e-acute в ISO-Latin1, поэтому он выполняется успешно с этим набором кодов.
Если вы не знаете набор кодов, в котором получаете строки, у вас небольшие проблемы. Было бы лучше, если бы для вашего протокола / приложения был выбран единый набор кодов (надеюсь, UTF-8), а затем вы бы просто отвергли те, которые не декодировались.
Если вы не можете этого сделать, вам понадобится эвристика.
Ответ 4
Потому что UTF-8 многобайтовый и в нем нет символа, соответствующего вашей комбинации \xe9 плюс следующий пробел.
Почему он должен быть успешным в обоих utf-8 и latin-1?
Вот как это же предложение должно быть в utf-8:
>>> o.decode('latin-1').encode("utf-8") 'a test of \xc3\xa9 char'