从 Python 2 移植到 Python 3:“utf-8 编解码器无法解码字节”
Porting from Python 2 to Python 3: 'utf-8 codec can't decode byte'
嘿,我试图将那个小片段从 2 移植到 Python 3。
Python 2:
def _download_database(self, url):
try:
with closing(urllib.urlopen(url)) as u:
return StringIO(u.read())
except IOError:
self.__show_exception(sys.exc_info())
return None
Python 3:
def _download_database(self, url):
try:
with closing(urllib.request.urlopen(url)) as u:
response = u.read().decode('utf-8')
return StringIO(response)
except IOError:
self.__show_exception(sys.exc_info())
return None
但我还是得到了
utf-8 codec can't decode byte 0x8f in position 12: invalid start byte
我需要使用 StringIO,因为它是一个 zip 文件,我想用那个函数解析它:
def _parse_zip(self, raw_zip):
try:
zip = zipfile.ZipFile(raw_zip)
filelist = map(lambda x: x.filename, zip.filelist)
db_file = 'IpToCountry.csv' if 'IpToCountry.csv' in filelist else filelist[0]
with closing(StringIO(zip.read(db_file))) as raw_database:
return_val = self.___parse_database(raw_database)
if return_val:
self._load_data()
except:
self.__show_exception(sys.exc_info())
return_val = False
return return_val
raw_zip 是 download_database func
的 return
如果您只对从函数返回流处理程序感兴趣(而不是要求解码内容),您可以使用 BytesIO
而不是 StringIO
:
from contextlib import closing
from io import BytesIO
from urllib.request import urlopen
url = 'http://www.google.com'
with closing(urlopen(url)) as u:
response = u.read()
print(BytesIO(response))
您发布的 link,http://software77.net/geo-ip?DL=2
正在尝试下载 zip
二进制文件。
- 您不应将二进制 blob 转换为
str
(只需使用 BytesIO
)
- 如果您确实有充分的理由这样做,请使用
latin-1
作为解码器。
utf-8 无法解码任意二进制数据。
utf-8 是一种字符编码,可用于将文本(例如,在 Python 3 中表示为 str
类型——Unicode 代码点序列)编码为 bytestring (bytes
type -- 字节序列([0, 255] 区间内的小整数)并将其解码回来。
utf-8 不是唯一的字符编码。存在与 utf-8 不兼容的字符编码。即使 .decode('utf-8')
没有引发异常;这并不意味着结果是正确的——你可能会得到 mojibake if you use a wrong character encoding to decode text. See .
您输入的是一个 zip 文件 -- 二进制数据不是文本,因此您不应尝试将其解码为文本。
Python 3 帮助您查找与混合二进制数据和文本相关的错误。 要将代码从 Python 2 移植到 Python 3,您应该了解文本 (Unicode) 与二进制数据 (字节) 的区别。
str
on Python 2 是可用于二进制数据和(编码)文本的字节串。除非 from __future__ import unicode_literals
存在; ''
文字在 Python 2 中创建字节串。 u''
创建 unicode
实例。在 Python 3 str
类型是 Unicode。 bytes
指的是 Python 3 和 Python 2.7 上的字节序列(bytes
是 Python 2 上 str
的别名)。 b''
在 Python 2/3 上创建 bytes
个实例。
urllib.request.urlopen(url)
returns 一个类似文件的对象(二进制文件),你可以按原样传递它 在某些情况下 例如,to decode remote gzipped content on-the-fly:
#!/usr/bin/env python3
import xml.etree.ElementTree as etree
from gzip import GzipFile
from urllib.request import urlopen, Request
with urlopen(Request("http://smarkets.s3.amazonaws.com/oddsfeed.xml",
headers={"Accept-Encoding": "gzip"})) as response, \
GzipFile(fileobj=response) as xml_file:
for elem in getelements(xml_file, 'interesting_tag'):
process(elem)
ZipFile()
需要 seek()
-able 文件,因此您不能直接传递 urlopen()
。您必须先下载内容。您可以使用 io.BytesIO()
来包装它:
#!/usr/bin/env python3
import io
import zipfile
from urllib.request import urlopen
url = "http://www.pythonchallenge.com/pc/def/channel.zip"
with urlopen(url) as r, zipfile.ZipFile(io.BytesIO(r.read())) as archive:
print({member.filename: archive.read(member) for member in archive.infolist()})
StringIO()
是文本文件。它将 Unicode 存储在 Python 3.
嘿,我试图将那个小片段从 2 移植到 Python 3。
Python 2:
def _download_database(self, url):
try:
with closing(urllib.urlopen(url)) as u:
return StringIO(u.read())
except IOError:
self.__show_exception(sys.exc_info())
return None
Python 3:
def _download_database(self, url):
try:
with closing(urllib.request.urlopen(url)) as u:
response = u.read().decode('utf-8')
return StringIO(response)
except IOError:
self.__show_exception(sys.exc_info())
return None
但我还是得到了
utf-8 codec can't decode byte 0x8f in position 12: invalid start byte
我需要使用 StringIO,因为它是一个 zip 文件,我想用那个函数解析它:
def _parse_zip(self, raw_zip):
try:
zip = zipfile.ZipFile(raw_zip)
filelist = map(lambda x: x.filename, zip.filelist)
db_file = 'IpToCountry.csv' if 'IpToCountry.csv' in filelist else filelist[0]
with closing(StringIO(zip.read(db_file))) as raw_database:
return_val = self.___parse_database(raw_database)
if return_val:
self._load_data()
except:
self.__show_exception(sys.exc_info())
return_val = False
return return_val
raw_zip 是 download_database func
的 return如果您只对从函数返回流处理程序感兴趣(而不是要求解码内容),您可以使用 BytesIO
而不是 StringIO
:
from contextlib import closing
from io import BytesIO
from urllib.request import urlopen
url = 'http://www.google.com'
with closing(urlopen(url)) as u:
response = u.read()
print(BytesIO(response))
您发布的 link,http://software77.net/geo-ip?DL=2
正在尝试下载 zip
二进制文件。
- 您不应将二进制 blob 转换为
str
(只需使用BytesIO
) - 如果您确实有充分的理由这样做,请使用
latin-1
作为解码器。
utf-8 无法解码任意二进制数据。
utf-8 是一种字符编码,可用于将文本(例如,在 Python 3 中表示为 str
类型——Unicode 代码点序列)编码为 bytestring (bytes
type -- 字节序列([0, 255] 区间内的小整数)并将其解码回来。
utf-8 不是唯一的字符编码。存在与 utf-8 不兼容的字符编码。即使 .decode('utf-8')
没有引发异常;这并不意味着结果是正确的——你可能会得到 mojibake if you use a wrong character encoding to decode text. See .
您输入的是一个 zip 文件 -- 二进制数据不是文本,因此您不应尝试将其解码为文本。
Python 3 帮助您查找与混合二进制数据和文本相关的错误。 要将代码从 Python 2 移植到 Python 3,您应该了解文本 (Unicode) 与二进制数据 (字节) 的区别。
str
on Python 2 是可用于二进制数据和(编码)文本的字节串。除非 from __future__ import unicode_literals
存在; ''
文字在 Python 2 中创建字节串。 u''
创建 unicode
实例。在 Python 3 str
类型是 Unicode。 bytes
指的是 Python 3 和 Python 2.7 上的字节序列(bytes
是 Python 2 上 str
的别名)。 b''
在 Python 2/3 上创建 bytes
个实例。
urllib.request.urlopen(url)
returns 一个类似文件的对象(二进制文件),你可以按原样传递它 在某些情况下 例如,to decode remote gzipped content on-the-fly:
#!/usr/bin/env python3
import xml.etree.ElementTree as etree
from gzip import GzipFile
from urllib.request import urlopen, Request
with urlopen(Request("http://smarkets.s3.amazonaws.com/oddsfeed.xml",
headers={"Accept-Encoding": "gzip"})) as response, \
GzipFile(fileobj=response) as xml_file:
for elem in getelements(xml_file, 'interesting_tag'):
process(elem)
ZipFile()
需要 seek()
-able 文件,因此您不能直接传递 urlopen()
。您必须先下载内容。您可以使用 io.BytesIO()
来包装它:
#!/usr/bin/env python3
import io
import zipfile
from urllib.request import urlopen
url = "http://www.pythonchallenge.com/pc/def/channel.zip"
with urlopen(url) as r, zipfile.ZipFile(io.BytesIO(r.read())) as archive:
print({member.filename: archive.read(member) for member in archive.infolist()})
StringIO()
是文本文件。它将 Unicode 存储在 Python 3.