如何修复:"UnicodeDecodeError:'ascii'编解码器无法解码字节"

fis*_*man 428 python python-2.7 chinese-locale

as3:~/ngokevin-site# nano content/blog/20140114_test-chinese.mkd
as3:~/ngokevin-site# wok
Traceback (most recent call last):
File "/usr/local/bin/wok", line 4, in
Engine()
File "/usr/local/lib/python2.7/site-packages/wok/engine.py", line 104, in init
self.load_pages()
File "/usr/local/lib/python2.7/site-packages/wok/engine.py", line 238, in load_pages
p = Page.from_file(os.path.join(root, f), self.options, self, renderer)
File "/usr/local/lib/python2.7/site-packages/wok/page.py", line 111, in from_file
page.meta['content'] = page.renderer.render(page.original)
File "/usr/local/lib/python2.7/site-packages/wok/renderers.py", line 46, in render
return markdown(plain, Markdown.plugins)
File "/usr/local/lib/python2.7/site-packages/markdown/init.py", line 419, in markdown
return md.convert(text)
File "/usr/local/lib/python2.7/site-packages/markdown/init.py", line 281, in convert
source = unicode(source)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position 1: ordinal not in range(128). -- Note: Markdown only accepts unicode input!
Run Code Online (Sandbox Code Playgroud)

怎么解决?

在其他一些基于python的静态博客应用程序中,中文帖子可以成功发布.比如这个应用程序:http://github.com/vrypan/bucket3.在我的网站http://bc3.brite.biz/中,中文帖子可以成功发布.

Ala*_*ack 535

不要使用流行的答案(reload)

这是一个讨厌的黑客(你必须使用的原因UnicodeDecodeError: 'ascii' codec can't decode byte)只会掩盖问题并阻碍你迁移到Python 3.x. 了解问题,修复根本原因并享受Unicode zen.请参阅为什么我们不应该在py脚本中使用sys.setdefaultencoding("utf-8")?了解更多详情

tl; dr /快速修复

  • 不要随便解码/编码
  • 不要假设您的字符串是UTF-8编码的
  • 尝试在代码中尽快将字符串转换为Unicode字符串

Python 2.x中的Unicode Zen - 长版本

如果没有看到来源,就很难知道根本原因,所以我不得不一般地说.

str当您尝试将unicode()包含非ASCII 的Python 2.x转换为Unicode字符串而不指定原始字符串的编码时,通常会发生这种情况.

简而言之,Unicode字符串是完全独立的Python字符串类型,不包含任何编码.它们只保存Unicode 点代码,因此可以保存整个频谱中的任何Unicode点.字符串包含编码文本,beit UTF-8,UTF-16,ISO-8895-1,GBK,Big5等.字符串被解码为Unicode,Unicodes被编码为字符串.文件和文本数据始终以编码字符串传输.

Markdown模块作者可能会使用u(抛出异常的地方)作为代码其余部分的质量门 - 它将转换ASCII或将现有的Unicodes字符串重新包装为新的Unicode字符串.Markdown作者无法知道传入字符串的编码,因此在传递给Markdown之前,您将依赖于将字符串解码为Unicode字符串.

可以使用str字符串前缀在代码中声明Unicode 字符串.例如

>>> my_u = u'my ünicôdé str?ng'
>>> type(my_u)
<type 'unicode'>
Run Code Online (Sandbox Code Playgroud)

Unicode字符串也可能来自文件,数据库和网络模块.发生这种情况时,您无需担心编码.

陷阱

unicode()即使您没有显式调用,也可能会发生从Unicode 转换UnicodeDecodeError.

以下方案会导致café异常:

# Explicit conversion without encoding
unicode('€')

# New style format string into Unicode string
# Python will try to convert value string to Unicode first
u"The currency is: {}".format('€')

# Old style format string into Unicode string
# Python will try to convert value string to Unicode first
u'The currency is: %s' % '€'

# Append string to Unicode
# Python will try to convert string to Unicode first
u'The currency is: ' + '€'         
Run Code Online (Sandbox Code Playgroud)

例子

在下图中,您可以看到单词caf是如何编码的,以"UTF-8"或"Cp1252"编码,具体取决于终端类型.在这两个例子中,é只是常规的ascii.在UTF-8中,decode()使用两个字节进行编码.在"Cp1252"中,é是0xE9(这也恰好是Unicode点值(这不是巧合)).decode()调用正确并成功转换为Python Unicode: 正在转换为Python Unicode字符串的字符串图

在此图中,ascii调用with unicode()(与0x7F没有给出编码的调用相同).由于ASCII不能包含大于的字节UnicodeDecodeError,因此会抛出str异常:

正在使用错误的编码将字符串转换为Python Unicode字符串的图表

Unicode三明治

在您的代码中形成Unicode三明治是一种很好的做法,您可以将所有传入数据解码为Unicode字符串,使用Unicodes,然后u在出路时编码为s.这样可以避免担心代码中间的字符串编码.

输入/解码

源代码

如果需要在源代码中加入非ASCII,只需在字符串前加上a即可创建Unicode字符串io.例如

u'Zürich'
Run Code Online (Sandbox Code Playgroud)

要允许Python解码源代码,您需要添加一个编码头以匹配文件的实际编码.例如,如果您的文件编码为"UTF-8",您将使用:

# encoding: utf-8
Run Code Online (Sandbox Code Playgroud)

只有在源代码中包含非ASCII时才需要这样做.

通常从文件接收非ASCII数据.该encoding模块提供了一个TextWrapper,它使用给定的动态解码您的文件my_unicode_string.您必须使用正确的文件编码 - 它不容易被猜到.例如,对于UTF-8文件:

import io
with io.open("my_utf8_file.txt", "r", encoding="utf-8") as my_file:
     my_unicode_string = my_file.read() 
Run Code Online (Sandbox Code Playgroud)

UnicodeDecodeError那么适合传递给Markdown.如果read()来自该Content-type行,那么您可能使用了错误的编码值.

CSV文件

Python 2.7 CSV模块不支持非ASCII字符.但是,使用https://pypi.python.org/pypi/backports.csv即可获得帮助.

像上面一样使用它,但将打开的文件传递给它:

from backports import csv
import io
with io.open("my_utf8_file.txt", "r", encoding="utf-8") as my_file:
    for row in csv.reader(my_file):
        yield row
Run Code Online (Sandbox Code Playgroud)

数据库

大多数Python数据库驱动程序都可以以Unicode格式返回数据,但通常需要一些配置.始终使用Unicode字符串进行SQL查询.

MySQL的

在连接字符串中添加:

charset='utf8',
use_unicode=True
Run Code Online (Sandbox Code Playgroud)

例如

>>> db = MySQLdb.connect(host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")
Run Code Online (Sandbox Code Playgroud) PostgreSQL的

加:

psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
Run Code Online (Sandbox Code Playgroud)

HTTP

网页可以使用任何编码进行编码.的charset报头应包含一个response.text字段在编码暗示.然后可以针对该值手动解码内容.或者,Python-Requests返回Unicodes my_string.decode(encoding).

手动

如果你必须手动解码字符串,你可以简单地做encoding,UnicodeDecodeError适当的编码在哪里.Python 2.x支持的编解码器在这里给出:标准编码.再说一遍,如果你得到print那么你可能得到了错误的编码.

三明治的肉

像普通人一样使用Unicodes.

产量

stdout /打印

locale通过stdout流写入.Python尝试在stdout上配置编码器,以便将Unicodes编码为控制台的编码.例如,如果Linux外壳的en_GB.UTF-8UTF-8,输出将被编码以PYTHONIOENCODING.在Windows上,您将被限制为8位代码页.

配置不正确的控制台(例如损坏的区域设置)可能会导致意外的打印错误.io.open环境变量可以强制stdout的编码.

就像输入一样,str可以用来透明地将Unicodes转换为编码的字节串.

数据库

读取的相同配置将允许直接写入Unicodes.

Python 3

Python 3不再像Python 2.x那样具有Unicode功能,但是常规str现在是一个Unicode字符串,bytes现在是旧的.decode().

默认编码现在是UTF-8,因此如果您open()没有给出编码的字节字符串,Python 3将使用UTF-8编码.这可能解决了50%的人的Unicode问题.

此外,str默认情况下以文本模式运行,因此返回已解码的sys.setdefaultencoding('utf8')(Unicode).编码源自您的语言环境,它在Un*x系统上为UTF-8,在Windows框上为8位代码页,如windows-1251.

  • 对于寻找Python 2答案的人来说,一个更有用的TLDR:使用`io.open`来读/写文件,使用`from __future__ import unicode_literals`,配置其他数据输入/输出(例如数据库)来使用unicode. (2认同)

fis*_*man 489

终于我明白了:

as3:/usr/local/lib/python2.7/site-packages# cat sitecustomize.py
# encoding=utf8  
import sys  

reload(sys)  
sys.setdefaultencoding('utf8')
Run Code Online (Sandbox Code Playgroud)

我来看看:

as3:~/ngokevin-site# python
Python 2.7.6 (default, Dec  6 2013, 14:49:02)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.getdefaultencoding()
'utf8'
>>>
Run Code Online (Sandbox Code Playgroud)

上面显示了python的默认编码是utf8.然后错误就不复存在了.

  • 谢谢!但是为什么我们需要在导入后重新加载sys? (37认同)
  • 我尝试这个但它无法永久改变编码.一旦退出python控制台并重新开始,编码仍然是相同的 (7认同)
  • @DmitryNarkevich,因为[Illusive setdefaultencoding](http://www.ianbicking.org/illusive-setdefaultencoding.html)函数.它在Python启动时被删除,因为它显然应该从一开始就不应该成为正确版本的一部分. (6认同)
  • @miraculixx Python 3的默认编码是UTF-8,Unicode字符串作为默认的`str`,因此它不会过期.在Python 2.x中,Unicode处于转换状态,因此在将字节转换为Unicodes时假定编码会很危险.因此,Py2的默认ASCII编码是有意选择的,为什么更改默认编码需要故意重新加载`sys`.在Py2中消除编码错误的正确方法是在需要转换时明确地将字符串解码并编码(字节)到Unicode - 而不仅仅是假设字符串是UTF-8编码的. (4认同)
  • Uf,有史以来最好的答案.直到这一刻,我才被迫在每个文件的开头加入"# - * - coding:utf-8 - * - ".这样更容易,并且作为魅力 (2认同)
  • 这意味着您没有修复根本原因.您刚修补了任何隐含的转化 (2认同)

Gre*_*ade 124

这是经典的"unicode问题".我相信解释这一点超出了StackOverflow答案的范围,可以完全解释发生的事情.

这里有很好的解释.

在非常简短的总结中,您已经将一些被解释为字节串的东西传递给需要将其解码为Unicode字符的东西,但是默认的编解码器(ascii)失败了.

我指出的演示文稿提供了避免这种情况的建议.使您的代码成为"unicode三明治".在Python 2中,使用"from __future__ import unicode_literals"会有所帮助.

更新:如何修复代码:

好的 - 在变量"source"中你有一些字节.从你的问题不清楚他们是如何进入那里的 - 也许你是从网络表格中读到的?在任何情况下,它们都不是用ascii编码的,但python试图将它们转换为unicode,假设它们是.您需要明确告诉它编码是什么.这意味着您需要知道编码是什么!这并不总是容易的,它完全取决于这个字符串的来源.您可以尝试一些常见的编码 - 例如UTF-8.你告诉unicode()编码作为第二个参数:

source = unicode(source, 'utf-8')
Run Code Online (Sandbox Code Playgroud)

  • 使用`currentFile = open(filename,'rt',encoding ='latin1')`或`currentFile = open(filename,'rt',encoding ='utf-8')` - 见这里:http:// stackoverflow. COM/A /二百零四万七千四百四十二分之二千三百九十一万七千七百九十九 (11认同)
  • 奇怪......经过一年多的积极反馈,突然两次反对票......嗯? (7认同)
  • greenasjade先生:我应该把"source = unicode(source,'utf-8')"放在哪里? (2认同)

小智 42

在某些情况下,当您检查默认编码(print sys.getdefaultencoding())时,它会返回您使用的ASCII.如果更改为UTF-8,则它不起作用,具体取决于变量的内容.我找到了另一种方式:

import sys
reload(sys)  
sys.setdefaultencoding('Cp1252')
Run Code Online (Sandbox Code Playgroud)


Zoe*_*e L 21

我正在搜索以解决以下错误消息:

unicodedecodeerror:'ascii'编解码器无法解码5454位的字节0xe2:序号不在范围内(128)

我终于通过指定'encoding'来修复它:

f = open('../glove/glove.6B.100d.txt', encoding="utf-8")
Run Code Online (Sandbox Code Playgroud)

希望它也可以帮到你.


小智 19

"UnicodeDecodeError: 'ascii' codec can't decode byte"
Run Code Online (Sandbox Code Playgroud)

导致此错误的原因:input_string必须是unicode但是给出了str

"TypeError: Decoding Unicode is not supported"
Run Code Online (Sandbox Code Playgroud)

此错误的原因:尝试将unicode input_string转换为unicode


因此,首先检查您的input_string str是否必要,并在必要时转换为unicode:

if isinstance(input_string, str):
   input_string = unicode(input_string, 'utf-8')
Run Code Online (Sandbox Code Playgroud)

其次,上面只是更改类型但不删除非ascii字符.如果要删除非ascii字符:

if isinstance(input_string, str):
   input_string = input_string.decode('ascii', 'ignore').encode('ascii') #note: this removes the character and encodes back to string.

elif isinstance(input_string, unicode):
   input_string = input_string.encode('ascii', 'ignore')
Run Code Online (Sandbox Code Playgroud)


mir*_*ixx 9

我发现最好总是转换为unicode - 但这很难实现,因为在实践中你必须检查并将每个参数转换为你编写的每个函数和方法,包括某种形式的字符串处理.

所以我提出了以下方法来保证来自任一输入的unicodes或字符串.简而言之,包括并使用以下lambdas:

# guarantee unicode string
_u = lambda t: t.decode('UTF-8', 'replace') if isinstance(t, str) else t
_uu = lambda *tt: tuple(_u(t) for t in tt) 
# guarantee byte string in UTF8 encoding
_u8 = lambda t: t.encode('UTF-8', 'replace') if isinstance(t, unicode) else t
_uu8 = lambda *tt: tuple(_u8(t) for t in tt)
Run Code Online (Sandbox Code Playgroud)

例子:

text='Some string with codes > 127, like Zürich'
utext=u'Some string with codes > 127, like Zürich'
print "==> with _u, _uu"
print _u(text), type(_u(text))
print _u(utext), type(_u(utext))
print _uu(text, utext), type(_uu(text, utext))
print "==> with u8, uu8"
print _u8(text), type(_u8(text))
print _u8(utext), type(_u8(utext))
print _uu8(text, utext), type(_uu8(text, utext))
# with % formatting, always use _u() and _uu()
print "Some unknown input %s" % _u(text)
print "Multiple inputs %s, %s" % _uu(text, text)
# but with string.format be sure to always work with unicode strings
print u"Also works with formats: {}".format(_u(text))
print u"Also works with formats: {},{}".format(*_uu(text, text))
# ... or use _u8 and _uu8, because string.format expects byte strings
print "Also works with formats: {}".format(_u8(text))
print "Also works with formats: {},{}".format(*_uu8(text, text))
Run Code Online (Sandbox Code Playgroud)

以下是对此的更多推理.

  • @miraculixx对不起,我不是故意像个混蛋.每次在代码中使用字符串时担心解码和编码都是不必要的. (3认同)

Gan*_*hah 8

有同样的错误,这解决了我的错误。谢谢!python 2 和 python 3 在 unicode 处理方面的不同使得腌制文件与加载非常不兼容。所以使用 python pickle 的编码参数。当我尝试从 python 3.7 打开腌制数据时,下面的链接帮助我解决了类似的问题,而我的文件最初保存在 python 2.x 版本中。 https://blog.modest-destiny.com/posts/python-2-and-3-compatible-pickle-save-and-load/ 我在我的脚本中复制了 load_pickle 函数并在加载我的脚本时调用了 load_pickle(pickle_file) input_data 像这样:

input_data = load_pickle("my_dataset.pkl")
Run Code Online (Sandbox Code Playgroud)

load_pickle 函数在这里:

def load_pickle(pickle_file):
    try:
        with open(pickle_file, 'rb') as f:
            pickle_data = pickle.load(f)
    except UnicodeDecodeError as e:
        with open(pickle_file, 'rb') as f:
            pickle_data = pickle.load(f, encoding='latin1')
    except Exception as e:
        print('Unable to load data ', pickle_file, ':', e)
        raise
    return pickle_data
Run Code Online (Sandbox Code Playgroud)


小智 6

Encode将unicode对象转换为字符串对象.我想你正在尝试编码一个字符串对象.首先将结果转换为unicode对象,然后将该unicode对象编码为'utf-8'.例如

    result = yourFunction()
    result.decode().encode('utf-8')
Run Code Online (Sandbox Code Playgroud)


Fab*_*lao 6

我有同样的错误,URL 包含非 ascii 字符(值 > 128 的字节),我的解决方案:

url = url.decode('utf8').encode('utf-8')
Run Code Online (Sandbox Code Playgroud)

注意:utf-8、utf8 只是别名。仅使用 'utf8' 或 'utf-8' 应该以相同的方式工作

就我而言,在 Python 2.7 中对我有用,我想这个赋值改变了str内部表示中的“某些东西” ——即,它强制对支持的字节序列进行正确解码,url最后将字符串放入utf-8 str中所有的魔法都在正确的地方。Python 中的 Unicode 对我来说是黑魔法。希望有用


sar*_*n3h 6

这对我有用:

    file = open('docs/my_messy_doc.pdf', 'rb')
Run Code Online (Sandbox Code Playgroud)


Rei*_*amn 5

我遇到了同样的问题,但它不适用于 Python 3。我遵循了这个,它解决了我的问题:

enc = sys.getdefaultencoding()
file = open(menu, "r", encoding = enc)
Run Code Online (Sandbox Code Playgroud)

您必须在读取/写入文件时设置编码。


ver*_*vas 5

为了在Ubuntu安装中的操作系统级别解决此问题,请检查以下内容:

$ locale charmap
Run Code Online (Sandbox Code Playgroud)

如果你得到

locale: Cannot set LC_CTYPE to default locale: No such file or directory
Run Code Online (Sandbox Code Playgroud)

代替

UTF-8
Run Code Online (Sandbox Code Playgroud)

然后设置LC_CTYPELC_ALL像这样:

locale: Cannot set LC_CTYPE to default locale: No such file or directory
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

855482 次

最近记录:

6 年 前