在 Python 3 中处理文件内容时出现“TypeError:需要一个类似字节的对象,而不是‘str’”
- 2024-12-12 08:41:00
- admin 原创
- 152
问题描述:
我最近刚刚迁移到 Python 3.5。此代码在 Python 2.7 中运行正常:
with open(fname, 'rb') as f:
lines = [x.strip() for x in f.readlines()]
for line in lines:
tmp = line.strip().lower()
if 'some-pattern' in tmp: continue
# ... code
但是在 3.5 中if 'some-pattern' in tmp: continue
,我收到一条错误消息:
TypeError: a bytes-like object is required, not 'str'
我无法使用.decode()
两侧的来修复此问题in
,也无法使用 来修复此问题
if tmp.find('some-pattern') != -1: continue
哪里出了问题?我该如何修复?
解决方案 1:
您以二进制模式打开了该文件:
with open(fname, 'rb') as f:
这意味着从文件读取的所有数据都作为bytes
对象返回,而不是str
。然后您不能在包含测试中使用字符串:
if 'some-pattern' in tmp: continue
您必须使用一个bytes
对象来测试tmp
:
if b'some-pattern' in tmp: continue
'rb'
或者通过将模式替换为,将文件作为文本文件打开'r'
。
解决方案 2:
您可以使用以下方式对字符串进行编码.encode()
例子:
'Hello World'.encode()
正如错误所描述的,为了将字符串写入文件,您需要先将其编码为字节状对象,然后encode()
将其编码为字节字符串。
解决方案 3:
就像前面提到的那样,您正在以二进制模式读取文件,然后创建一个字节列表。在下面的for循环中,您正在将字符串与字节进行比较,而这正是代码失败的地方。
在将字节添加到列表的同时对其进行解码应该可以正常工作。更改后的代码应如下所示:
with open(fname, 'rb') as f:
lines = [x.decode('utf8').strip() for x in f.readlines()]
字节类型是在 Python 3 中引入的,这就是你的代码可以在 Python 2 中运行的原因。在 Python 2 中没有字节的数据类型:
>>> s=bytes('hello')
>>> type(s)
<type 'str'>
解决方案 4:
您必须从 wb 更改为 w:
def __init__(self):
self.myCsv = csv.writer(open('Item.csv', 'wb'))
self.myCsv.writerow(['title', 'link'])
到
def __init__(self):
self.myCsv = csv.writer(open('Item.csv', 'w'))
self.myCsv.writerow(['title', 'link'])
更改此设置后,错误消失,但无法写入文件(就我而言)。所以说,我还是没有答案?
来源:如何删除 ^M
更改为“rb”会给我带来另一个错误:io.UnsupportedOperation:write
解决方案 5:
将 encode() 函数与单引号中给出的硬编码字符串值一起使用。
例子:
file.write(answers[i] + '
'.encode())
或者
line.split(' +++$+++ '.encode())
解决方案 6:
对于这个小例子,添加 only b
before
`'GET http://www.py4inf.com/code/romeo.txt HTTP/1.0
'`解决了我的问题:
import socket
mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysock.connect(('www.py4inf.com', 80))
mysock.send(b'GET http://www.py4inf.com/code/romeo.txt HTTP/1.0
')
while True:
data = mysock.recv(512)
if (len(data) < 1):
break
print (data);
mysock.close()
字符串文字前面的“b”字符起什么作用?
解决方案 7:
您以二进制模式打开了该文件:
以下代码将抛出 TypeError:需要一个类似字节的对象,而不是“str”。
for line in lines:
print(type(line))# <class 'bytes'>
if 'substring' in line:
print('success')
以下代码将起作用 - 您必须使用decode()函数:
for line in lines:
line = line.decode()
print(type(line))# <class 'str'>
if 'substring' in line:
print('success')
解决方案 8:
尝试以文本形式打开文件:
with open(fname, 'rt') as f:
lines = [x.strip() for x in f.readlines()]
另外,这里是 Python 3.x 官方页面上的链接:
io - 用于处理流的核心工具。
这是打开函数:打开
如果您确实想将其作为二进制文件处理,那么请考虑对字符串进行编码。
解决方案 9:
概括
Python 2.x 鼓励了许多关于文本处理的坏习惯。特别是,它的命名类型str
实际上并不代表符合 Unicode 标准的文本(该类型是unicode
),并且默认的“字符串文字”实际上会生成原始字节序列 - 有一些方便的函数可以将其视为字符串,如果您可以假设“代码页”样式的编码。
在 3.x 中,“字符串文字”现在生成实际的字符串,并且内置功能不再在两种类型之间进行任何隐式转换。因此,相同的代码现在有一个TypeError
,因为文字和变量的类型不兼容。要解决此问题,必须替换或转换其中一个值,以便类型匹配。
Python 文档有一个关于如何正确使用 Unicode 的极其详细的指南。
在问题中的示例中,输入文件被处理为包含文本。因此,首先应该以文本模式打开文件。即使在 2.x 中,以二进制模式打开文件的唯一好理由也是为了避免通用换行符转换;在 3.x 中,这是通过newline
在以文本模式打开文件时指定关键字参数来实现的。
要正确地将文件读取为文本,需要知道文本编码,它在代码中由(字符串)名称指定。编码iso-8859-1
是一种安全的后备;它按顺序分别解释每个字节,表示前 256 个 Unicode 代码点之一(因此它永远不会因数据无效而引发异常)。utf-8
在撰写本文时更为常见,但它不接受任意数据。(但是,在许多情况下,对于英文文本,区别并不重要;这两种编码以及更多编码都是 ASCII 的超集。)
因此:
with open(fname, 'r', newline='
', encoding='iso-8859-1') as f:
lines = [x.strip() for x in f.readlines()]
# proceed as before
# If the results are wrong, take additional steps to ascertain the correct encoding
从 2.x 迁移到 3.x 时如何产生错误
在 2.x 中,'some-pattern'
创建一个str
,即程序员可能假装为文本的字节序列str
。类型与类型相同bytes
,但不同于正确表示文本的类型。提供了许多方法来将此数据视为文本,但这不是文本的正确表示。假定unicode
每个值的含义为文本字符(编码)。(为了使原始数据看起来像“文本”,有时会在和类型之间进行隐式转换。但是,这会导致令人困惑的错误 - 例如从尝试编码或反之亦然)。str
`unicode`UnicodeDecodeError
在 3.x 中,'some-pattern'
创建了也称为str
;但现在str
指的是使用 Unicode 的、正确表示文本的字符串类型。 (unicode
不再用作类型名称,仅指bytes
字节序列类型。)对其进行了一些更改以将bytes
其与带有假定编码的文本解释分离(特别是,bytes
现在对对象进行索引会生成一个int
,而不是一个 1 元素bytes
),但许多奇怪的遗留方法仍然存在(包括那些甚至很少用于实际字符串的方法,如zfill
)。
为什么这会导致问题
数据tmp
是一个bytes
实例。它来自二进制源:在本例中,是使用'b'
文件模式打开的文件。在其他情况下,它可能来自原始网络套接字、使用urllib
或类似方式发出的 Web 请求,或者其他一些 API 调用。
这意味着它无法与字符串结合做任何有意义的事情。字符串的元素是 Unicode 代码点(即,以表示所有世界语言和许多其他符号的通用形式表示大部分文本字符的抽象)。a 的元素是bytes
字节。(具体来说,在 3.x 中,它们被解释为范围从 0 到 255(含)的无符号整数。)
当代码迁移时,文字'some-pattern'
从描述变为bytes
描述文本。因此,代码从进行合法比较(字节序列到字节序列)变为进行非法比较(字符串到字节序列)。
解决问题
为了对字符串和字节序列进行操作 - 无论是使用 来检查相等性==
、使用 来按字典顺序比较<
、使用 来搜索子字符串in
、使用 来连接+
还是其他任何操作 - 都必须将字符串转换为字节序列,反之亦然。通常,只有其中之一才是正确、合理的答案,并且取决于上下文。
修复源头
有时,其中一个值一开始就可能被视为“错误”。例如,如果读取文件的目的是为了得到文本,那么它应该以文本模式打开。在 3.x 中,文件编码可以简单地作为关键字参数传递给encoding
,open
并且可以无缝处理到 Unicode 的转换,而无需将二进制文件提供给显式翻译步骤(因此,通用换行符处理仍然可以无缝进行)。
就原始示例而言,它可能看起来像:
with open(fname, 'r') as f:
lines = [x.strip() for x in f.readlines()]
此示例假设文件采用与平台相关的默认编码。这通常适用于在同一台计算机上以简单方式创建的文件。但在一般情况下,必须知道数据的编码才能正确处理数据。
如果已知编码是 UTF-8,则可简单指定:
with open(fname, 'r', encoding='utf-8') as f:
lines = [x.strip() for x in f.readlines()]
类似地,本应为字节文字的字符串文字只是缺少前缀:要使字节序列表示整数值[101, 120, 97, 109, 112, 108, 101]
(即字母的 ASCII 值example
),请写入字节文字b'example'
,而不是字符串文字“example” 。反之亦然。
就原始示例而言,它看起来像这样:
if b'some-pattern' in tmp:
这里有一个内置的安全措施:字节文字语法只允许使用 ASCII 字符,因此不管源文件的编码是b'ëxãmþlê'
什么,类似的内容都会被捕获为SyntaxError
(因为不清楚指的是哪些字节值;在旧的隐式编码方案中,ASCII 范围已经确立,但其他一切都悬而未决。)当然,元素代表值bytes
128..255 的文字仍然可以通过对x
这些值进行转义来编写:例如,b'xebxxe3mxfelxea'
将产生与 Latin-1(ISO 8859-1)编码中的文本相对应的字节序列ëxãmþlê
。
适当时转换
字节序列和文本之间的转换只有在确定了编码后才有可能。一直以来都是如此;我们只是习惯于在本地假设一种编码,然后大多忽略我们已经这样做了。(东亚等地的程序员在历史上更了解这个问题,因为他们通常需要处理具有超过 256 个不同符号的脚本,因此他们的文本需要多字节编码。)
在 3.x 中,由于没有压力能够将字节序列隐式地视为具有假定编码的文本,因此后台没有隐式转换步骤。这意味着理解 API 很简单:字节是原始数据;因此,它们用于编码文本,这是一种抽象。因此,该.encode()
方法由 (表示文本) 提供str
,以便将文本编码为原始数据。同样,该方法由(表示字节序列).decode()
提供,以便将原始数据解码为文本。bytes
将这些应用到示例代码中,再次假设 UTF-8 编码是合适的,得到:
if 'some-pattern'.encode('utf-8') in tmp:
和
if 'some-pattern' in tmp.decode('utf-8'):
解决方案 10:
当我尝试将字符(或字符串)转换为时出现此错误bytes
,使用 Python 2.7 的代码如下:
# -*- coding: utf-8 -*-
print(bytes('ò'))
这是Python 2.7处理Unicode字符的方式。
这不适用于 Python 3.6,因为bytes
需要额外的编码参数,但这可能有点棘手,因为不同的编码可能会输出不同的结果:
print(bytes('ò', 'iso_8859_1')) # prints: b'xf2'
print(bytes('ò', 'utf-8')) # prints: b'xc3xb2'
就我而言,我必须使用iso_8859_1
编码字节来解决这个问题。
解决方案 11:
从网页抓取数据时有时会出现此特定错误。特别是,如果您使用requests
库获取数据,.content
则返回字节对象,而.text
返回字符串。因此,如果您想将内容读取为字符串(并执行字符串操作,例如in
等.split()
),请.text
改用。
import requests
x = requests.get('https://w3schools.com/python/demopage.htm')
'Page' in x.content # <---- TypeError: a bytes-like object is required, not 'str'
'Page' in x.text # <---- OK
type(x.content) # <class 'bytes'>
type(x.text) # <class 'str'>
在标准urllib
模块中,数据只能作为字节对象返回,在这种情况下,decode()
方法可能有助于将字节对象转换为字符串。
from urllib import request
y = request.urlopen('https://w3schools.com/python/demopage.htm')
z = y.read()
print(type(z)) # <class 'bytes'>
decoded_z = z.decode('utf-8') # <---- convert to string
'Page' in z # <----- TypeError
'Page' in decoded_z # <----- OK