在 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 = li...

问题描述:

我最近刚刚迁移到 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 bbefore
`'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 中,文件编码可以简单地作为关键字参数传递给encodingopen并且可以无缝处理到 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 范围已经确立,但其他一切都悬而未决。)当然,元素代表值bytes128..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
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用