UnicodeDecodeError: 'ascii'编解码器无法解码第2个位置的字节0xd1,因为它不在128的范围内。

128

我正在尝试处理一个非常大的数据集,其中包含一些非标准字符。根据工作规范,我需要使用Unicode,但我感到困惑(并且很可能做错了一切)。

我使用以下方式打开CSV文件:

 15     ncesReader = csv.reader(open('geocoded_output.csv', 'rb'), delimiter='\t', quotechar='"')

然后,我尝试使用以下方式进行编码:
name=school_name.encode('utf-8'), street=row[9].encode('utf-8'), city=row[10].encode('utf-8'), state=row[11].encode('utf-8'), zip5=row[12], zip4=row[13],county=row[25].encode('utf-8'), lat=row[22], lng=row[23])

我会编码除了经度和纬度以外的所有内容,因为它们需要被发送到API。当我运行程序将数据集解析成可用的格式时,我会得到以下Traceback。

Traceback (most recent call last):
  File "push_into_db.py", line 80, in <module>
    main()
  File "push_into_db.py", line 74, in main
    district_map = buildDistrictSchoolMap()
  File "push_into_db.py", line 32, in buildDistrictSchoolMap
    county=row[25].encode('utf-8'), lat=row[22], lng=row[23])
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd1 in position 2: ordinal not in range(128)

我想告诉您,我正在使用Python 2.7.2,并且这是构建在Django 1.4上的应用程序的一部分。我已经阅读了几篇关于此主题的文章,但似乎没有一个直接适用的。任何帮助都将不胜感激。

您可能还想知道,一些非标准字符引起了问题,例如Ñ和可能的É。


1
你的原始文件编码是什么?我认为你应该根据原始编码进行解码,然后转换为utf-8。 - xiao 啸
可能是重复的问题:编码出现 "'ascii' codec can't encode character … ordinal not in range(128)" 错误 [编者注:我相信还有大约无数个类似的问题。] - Karl Knechtel
13个回答

165

Unicode与UTF-8不相等。后者只是前者的一种编码方式。

你的做法是错误的。你正在读取UTF-8编码的数据,所以你需要将UTF-8编码的字符串解码成Unicode字符串。

所以只需使用.decode替换.encode,这样就可以正常运行了(如果你的.csv文件是UTF-8编码的)。

但无需感到羞愧。我敢打赌,三五个程序员最初理解这个问题时都会有困难,甚至更多 ;)

更新: 如果你的输入数据不是UTF-8编码的,则当然需要使用适当的编码来.decode()。如果没有给出任何编码,Python会默认使用ASCII,但这显然无法处理非ASCII字符。


2
错误的原因是Python试图自动从默认编码ASCII解码它,以便可以按照指定的UTF-8进行编码。由于数据不是有效的ASCII,所以无法正常工作。 - agf
7
没问题,但如果它是UTF8编码的数据(我猜是),那么.decode('utf-8')应该就可以解决问题了,对吧? - ch3ka
当然,你可能是对的。我只是在解释为什么在这种情况下会出现特定的错误。 - agf
1
太好了!非常感谢。原来是 .decode('latin-1') -- 这很有道理,因为是Ñ 给我带来的问题。再次感谢! - jelkimantis
1
你的解决方案对一些情况有效,但如果我使用它,则会出现另一个错误“'ascii' codec can't encode character u'\xf1' in position 2: ordinal not in range(128)” - Vikash Mishra
这并不总是正确的。第二个答案对我有用。 - Yasin

99

只需将以下行添加到您的代码中:

1. Python2

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

2.Python3

import sys
from importlib import reload
reload(sys)
sys.setdefaultencoding('utf-8')

12
在Python 3中似乎无法工作:AttributeError: module 'sys'没有'setdefaultencoding'属性。 - skjerns
1
它适用于我的Python 2.7,注意,需要使用reload(sys),否则,setdefaultencoding将无法访问。 - Yu Shen
1
那是让我在众多 Stack Overflow 问题中成功的唯一方法。非常感谢! - Freedo
1
名称'reload'未定义。 - Davide
1
对于Python3,请参考@Skrmnghrd的答案。 - Aman Jain
显示剩余2条评论

45

对于Python 3用户,您可以执行以下操作

with open(csv_name_here, 'r', encoding="utf-8") as f:
    #some codes

它也可以在Flask中使用 :)


2
这是我第一次通过这里帮助别人。知道我有所帮助,感觉很好:) - Skrmnghrd
1
你也帮了我一个大忙 :) 所有其他的答案都不能用于文件读取。现在我需要找出如何修复它以进行写入 ;) - user2194898
1
你能发给我你代码的链接吗?我会尽力帮忙。 - Skrmnghrd
1
谢谢!我忘记包含 'encoding="utf-8"' 部分了! - Bilguun

10
主要原因是Python默认编码为ASCII,如果使用encode('utf8')编码的字符串数据包含ASCII范围之外的字符,例如像 'hgvcj터파크387' 这样的字符串,则会抛出错误,因为该字符串不符合预期的编码格式。
如果您正在使用早于版本3.5的Python版本,则可靠的解决方法是将Python默认编码设置为utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
name = school_name.encode('utf8')

这种方式使得Python能够预测字符串中超出ASCII范围的字符。

然而,如果您使用的是Python 3.5或更高版本,则reload()函数不可用,因此您需要使用decode进行修复。

name = school_name.decode('utf8').encode('utf8')

你的答案和我的有什么不同? - khelili miliana
2
更详细一些。人们通常会发现有关因果细节的信息很有帮助。顺便说一下,你的代码是有效的,没有任何贬低的意思。 - Temi Fakunle
1
reload 在 Python 3 中可用,您只需要导入它。从 imp 导入 reload。 - Meow
@Meow,但是在Python 3中没有sys.setdefaultencoding。因此,在兼容py2\py3的情况下,可以进行一些检查,例如sys.getdefaultencoding()。关于这个问题,我会非常感激您给出一些建议。 https://dev59.com/VF4c5IYBdhLWcg3wQoY5 - Konst54

5

使用locale命令检查您正在使用的语言环境。如果不是en_US.UTF-8,可以像这样更改

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales

如果您没有权限进行此操作,您可以像这样运行所有 Python 代码:参考链接

PYTHONIOENCODING="UTF-8" python3 ./path/to/your/script.py

或者在运行Python代码之前运行此命令。

export PYTHONIOENCODING="UTF-8"

要在shell中设置它,您需要运行该命令。


在我的情况下,我使用的是POSIX,Ubuntu默认语言环境,而不是en_US.UTF-8,因此我看到了这个输出:

$ locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

这导致Python以ASCII而不是UTF-8格式打开文件。

您可以通过以下方式检查Python使用的区域设置:

>>> import locale
>>> locale.getpreferredencoding(False)
'ANSI_X3.4-1968'

locale.getpreferredencoding(False)是一个函数,当您不提供编码时,open()函数会调用它。输出应该是'UTF-8',但在我的情况下,它是'ANSI_X3.4-1968',这是ASCII的一些变体


4

对于Python 3用户:

将编码从“ascii”更改为“latin1”可行。

此外,您可以尝试使用以下代码片段读取前10000个字节以自动查找编码:

import chardet  
with open("dataset_path", 'rb') as rawdata:  
            result = chardet.detect(rawdata.read(10000))  
print(result)

1

如果你在Python中处理文本时,它是Unicode文本,请注意它是Unicode。

设置text=u'unicode text'而不仅仅是text='unicode text'

这在我的情况下起作用了。


1
如果在创建或更新证书时运行Certbot时遇到此问题,请使用以下方法:
使用以下命令:grep -r -P '[^\x00-\x7f]' /etc/apache2 /etc/letsencrypt /etc/nginx 该命令将在注释中的一个.conf文件中找到有问题的字符“´”。删除它(您可以根据需要编辑注释),然后重新加载nginx,一切都会再次正常工作。
来源:https://github.com/certbot/certbot/issues/5236

1
处理Docker容器内的这个问题。 可能情况是(就像我的情况一样),您只需要生成本地化设置,而无需执行其他任何操作:
sudo locale-gen en_US en_US.UTF-8

在某些情况下,这对我来说已经足够了,因为本地化已经安装并配置好了。如果您需要安装本地化并进行配置,请将以下部分添加到您的Dockerfile中:
RUN apt update && apt install locales && \
    sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
    echo 'LANG="en_US.UTF-8"'>/etc/default/locale && \
    dpkg-reconfigure --frontend=noninteractive locales && \
    update-locale LANG=en_US.UTF-8

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
ENV LC_ALL en_US.UTF-8

我像这样进行了测试:

cat <<EOF > /tmp/test.txt
++*=|@#|¼üöäàéàè!´]]¬|¢|¢¬|{ł|¼½{}}
EOF

python3
import pathlib; pathlib.Path("/tmp/test.txt").read_text()

https://hub.docker.com/_/ubuntu 建议使用以下方式:RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8,然后 ENV LANG en_US.utf8。另外,它说你可能只需要执行 ENV LANG C.UTF-8 就可以了(参见“Locales”部分)。 - user3064538

0

在使用Pickle进行卸载时,我遇到了这个问题。尝试一下:

data = pickle.load(f,encoding='latin1')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接