TypeError:在Python3中写入文件时需要类似字节的对象,而不是'str'

mas*_*ore 497 python string byte file python-3.x

我最近迁移到了Py 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
Run Code Online (Sandbox Code Playgroud)

升级到3.5后,我得到了:

TypeError: a bytes-like object is required, not 'str'
Run Code Online (Sandbox Code Playgroud)

最后一行的错误(模式搜索代码).

我已尝试.decode()在语句的任何一侧使用该函数,也尝试过:

if tmp.find('some-pattern') != -1: continue
Run Code Online (Sandbox Code Playgroud)

- 无济于事.

我能够迅速解决几乎所有2:3的问题,但这个小小的陈述让我烦恼.

Mar*_*ers 471

您以二进制模式打开文件:

with open(fname, 'rb') as f:
Run Code Online (Sandbox Code Playgroud)

这意味着从文件读取的所有数据都作为bytes对象返回,而不是str.然后,您无法在包含测试中使用字符串:

if 'some-pattern' in tmp: continue
Run Code Online (Sandbox Code Playgroud)

你必须使用一个bytes对象进行测试tmp:

if b'some-pattern' in tmp: continue
Run Code Online (Sandbox Code Playgroud)

或者通过替换'rb'模式将文件作为文本文件打开'r'.

  • 如果您查看了ppl链接到的各种文档,您会发现Py2中的所有东西都“起作用”,因为默认字符串是字节,而在Py3中,默认字符串是Unicode,这意味着您每次执行I / O时,尤其是 在网络上,字节字符串是标准配置,因此您必须学习移动黑白Unicode和字节字符串(en / decode)。对于文件,我们现在有了“ r”和“ rb”(以及“ w”和“ a”)来帮助区分。 (7认同)
  • @wescpy:Python 2的'r'和vs''rb'` *太*,在二进制文件和文本文件的行为之间进行切换(例如翻译换行符和在某些平台上,如何对待EOF标记)。io库(在Python 3中提供了默认的I / O功能,但在Python 2中也提供了)现在*默认情况下也*解码*文本文件才是真正的改变。 (2认同)
  • @MartijnPieters:是的,同意。在2.x版本中,当必须在DOS / Windows上使用二进制文件时,我仅使用了'b'标志(因为二进制文件是POSIX的默认设置)。在3.x中使用`io`进行文件访问时,有双重目的是很好的。 (2认同)
  • @ericOnline `ZipFile.open()` 文档 [明确声明仅支持二进制模式](https://docs.python.org/3/library/zipfile.html#zipfile.ZipFile.open) (*访问成员存档作为**二进制**类似文件的对象*)。您可以将文件对象包装在 [`io.TextIOWrapper()`](https://docs.python.org/3/library/io.html#io.TextIOWrapper) 中以达到相同的效果。 (2认同)
  • @ericOnline 另外,当您可以直接迭代文件对象时,不要使用 `.readlines()` 。特别是当您只需要一行信息时。当可以在第一个缓冲块中找到该信息时,为什么要将所有内容读取到内存中? (2认同)

The*_*tou 158

您可以使用编码您的字符串 .encode()

例:

'Hello World'.encode()
Run Code Online (Sandbox Code Playgroud)

  • 这有助于base64抛出上述错误。 (4认同)
  • 这个注释在使用`fd.subprocess.Popen();的上下文中非常有用。fd.沟通(...);`。 (3认同)

小智 40

就像已经提到过的那样,您正在以二进制模式读取文件,然后创建一个字节列表.在您的以下for循环中,您将比较字符串与字节,这是代码失败的地方.

添加到列表时解码字节应该有效.更改的代码应如下所示:

with open(fname, 'rb') as f:
    lines = [x.decode('utf8').strip() for x in f.readlines()]
Run Code Online (Sandbox Code Playgroud)

字节类型是在Python 3中引入的,这就是为什么你的代码在Python 2中工作的原因.在Python 2中,没有字节的数据类型:

>>> s=bytes('hello')
>>> type(s)
<type 'str'>
Run Code Online (Sandbox Code Playgroud)

  • Python 2 确实有字节类型,只是容易混淆地称为“str”,而文本字符串的类型称为“unicode”。在Python 3中,他们改变了“str”的含义,使其与旧的“unicode”类型相同,并将旧的“str”重命名为“bytes”。他们还删除了一堆会自动尝试从一种转换为另一种的情况。 (2认同)

mec*_*373 19

你必须从wb改为w:

def __init__(self):
    self.myCsv = csv.writer(open('Item.csv', 'wb')) 
    self.myCsv.writerow(['title', 'link'])
Run Code Online (Sandbox Code Playgroud)

def __init__(self):
    self.myCsv = csv.writer(open('Item.csv', 'w'))
    self.myCsv.writerow(['title', 'link'])
Run Code Online (Sandbox Code Playgroud)

更改后,错误消失,但您无法写入文件(在我的情况下).毕竟,我没有答案?

来源:如何删除^ M.

更改为'rb'会给我带来另一个错误:io.UnsupportedOperation:write


小智 14

对于这个小例子: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\n\n')

while True:
    data = mysock.recv(512)
    if ( len(data) < 1 ) :
        break
    print (data);

mysock.close()
Run Code Online (Sandbox Code Playgroud)

在'GET http://www.py4inf.com/code/romeo.txt HTTP/1.0 \n \n' 之前添加"b" 解决了我的问题

  • *为什么*它有效?OP 已经离开大楼(“最后一次见到是在 5 年前”),所以也许其他人可以插话? (3认同)

Fer*_*ime 6

为什么不尝试以文本形式打开文件?

with open(fname, 'rt') as f:
    lines = [x.strip() for x in f.readlines()]
Run Code Online (Sandbox Code Playgroud)

另外这里是官方页面上 python 3.x 的链接:https : //docs.python.org/3/library/io.html 这是开放功能:https : //docs.python.org/3 /library/functions.html#open

如果您真的想将其作为二进制文件处理,请考虑对您的字符串进行编码。


Mat*_*ugi 5

您以二进制模式打开文件:

以下代码将引发TypeError:需要一个类似字节的对象,而不是'str'。

for line in lines:
    print(type(line))# <class 'bytes'>
    if 'substring' in line:
       print('success')
Run Code Online (Sandbox Code Playgroud)

以下代码将起作用-您必须使用encode()函数:

for line in lines:
    line = line.decode()
    print(type(line))# <class 'str'>
    if 'substring' in line:
       print('success')
Run Code Online (Sandbox Code Playgroud)


Shi*_*yya 5

与单引号中给出的硬编码字符串值一起使用encode()函数。

例如:

file.write(answers[i] + '\n'.encode())
Run Code Online (Sandbox Code Playgroud)

要么

line.split(' +++$+++ '.encode())
Run Code Online (Sandbox Code Playgroud)

  • *为什么*这有效? (2认同)

bit*_*aps 5

当我尝试将字符(或字符串)转换为bytes,出现此错误,代码与 Python 2.7 类似:

\n
# -*- coding: utf-8 -*-\nprint(bytes(\'\xc3\xb2\'))\n
Run Code Online (Sandbox Code Playgroud)\n

这是Python 2.7的方式处理Unicode字符时

\n

这不适用于 Python 3.6,因为bytes需要额外的编码参数,但这可能有点棘手,因为不同的编码可能会输出不同的结果:

\n
print(bytes(\'\xc3\xb2\', \'iso_8859_1\')) # prints: b\'\\xf2\'\nprint(bytes(\'\xc3\xb2\', \'utf-8\')) # prints: b\'\\xc3\\xb2\'\n
Run Code Online (Sandbox Code Playgroud)\n

就我而言,我必须使用iso_8859_1在编码字节时使用才能解决问题。

\n

  • 请注意,文件顶部的“coding”注释不会影响“bytes”或“encode”的工作方式,它只会更改 Python 源代码中字符的解释方式。 (3认同)

Kar*_*tel 5

概括

\n

Python 2.x 助长了 WRT 文本处理的许多坏习惯。特别是,其名为的类型str实际上并不表示符合 Unicode 标准的文本(该类型是unicode),并且默认的“字符串文字”实际上会生成一系列原始字节 - 带有一些方便的函数,可以将其视为字符串,如果您可以不用假设“代码页”样式编码。

\n

在 3.x 中,“字符串文字”现在生成实际的字符串,并且内置功能不再在两种类型之间进行任何隐式转换。因此,相同的代码现在有一个TypeError,因为文字和变量的类型不兼容。要解决此问题,必须替换或转换其中一个值,以便类型匹配。

\n

Python 文档有非常详细的正确使用 Unicode 的指南。

\n

在问题的示例中,输入文件被处理为包含文本。因此,该文件首先应该以文本模式打开。即使在 2.x 中,该文件也以二进制模式打开的唯一充分理由是避免通用换行符转换;在 3.x 中,这是通过newline在文本模式下打开文件时指定关键字参数来完成的。

\n

要正确地将文件读取为文本,需要知道文本编码,该编码在代码中通过(字符串)名称指定。编码iso-8859-1是一种安全的后备方案;它按顺序单独解释每个字节,代表前 256 个 Unicode 代码点之一(因此它永远不会因无效数据而引发异常)。utf-8截至撰写本文时,它更为常见,但它不接受任意数据。(但是,在许多情况下,对于英文文本,这种区别并不重要;这两种编码以及更多编码都是 ASCII 的超集。)

\n

因此:

\n
with open(fname, \'r\', newline=\'\\n\', encoding=\'iso-8859-1\') as f:\n    lines = [x.strip() for x in f.readlines()]\n\n# proceed as before\n# If the results are wrong, take additional steps to ascertain the correct encoding\n
Run Code Online (Sandbox Code Playgroud)\n

从 2.x 迁移到 3.x 时如何产生错误

\n

在 2.x 中,\'some-pattern\'创建一个str,即程序员可能会假装是文本的字节序列。str类型与类型相同bytes,但与正确表示文本的类型不同unicode。提供了许多方法来将此数据视为文本,但它不是文本的正确表示。假设每个值作为文本字符(编码)的含义。(为了使原始数据产生“文本”的错觉,有时会在 和类型之间进行隐式转换。但是,这会导致其自身的令人困惑的错误 - 例如获取strunicodeUnicodeDecodeError从尝试编码中获取数据,反之亦然)。

\n

在 3.x 中,\'some-pattern\'创建也称为str;的内容。但现在str表示使用 Unicode、正确表示文本的字符串类型。(unicode不再用作类型名称,仅指bytes字节序列类型。)进行了一些更改,以将bytes其与带有假定编码的文本解释分离(特别是bytes现在索引到对象结果是int,而不是 1-element bytes),但许多奇怪的遗留方法仍然存在(包括那些即使在实际字符串中也很少使用的方法,例如zfill)。

\n

为什么这会导致问题

\n

数据tmp是一个bytes实例。它来自二进制源:在本例中,是使用\'b\'文件模式打开的文件。在其他情况下,它可能来自原始网络套接字、使用urllib或类似方式发出的 Web 请求,或其他一些 API 调用。

\n

这意味着它不能与字符串结合做任何有意义的事情。字符串的元素是 Unicode 代码点(即,在很大程度上表示文本字符的抽象,以表示所有世界语言和许多其他符号的通用形式)。的元素bytes是字节。(特别是在 3.x 中,它们被解释为范围从 0 到 255(含)的无符号整数。)

\n

当代码迁移时,文字\'some-pattern\'从描述bytes, 变为描述文本。因此,代码从进行合法比较(字节序列到字节序列),到进行非法比较(字符串到字节序列)。

\n

解决问题

\n

为了对字符串和字节序列进行操作 - 无论是检查与 的相等性==、与 的词典比较<、与 的子字符串搜索in、与 的连接+还是其他任何操作 - 字符串必须转换为字节序列,或相反亦然。一般来说,只有其中之一才是正确、合理的答案,并且这取决于上下文。

\n

修复源

\n

有时,其中一个值一开始就被认为是“错误的”。例如,如果读取文件的目的是生成文本,那么应该以文本模式打开它。在 3.x 中,文件编码可以简单地作为encoding关键字参数传递给open,并且无缝处理到 Unicode 的转换,而无需将二进制文件提供给显式转换步骤(因此,通用换行符处理仍然无缝进行)。

\n

对于原始示例,可能如下所示:

\n
with open(fname, \'r\') as f:\n    lines = [x.strip() for x in f.readlines()]\n
Run Code Online (Sandbox Code Playgroud)\n

此示例假定文件采用依赖于平台的默认编码。这通常适用于在同一台计算机上以简单方式创建的文件。然而,在一般情况下,必须知道数据的编码才能正确使用它。

\n

例如,如果已知编码是 UTF-8,则可以简单指定:

\n
with open(fname, \'r\', encoding=\'utf-8\') as f:\n    lines = [x.strip() for x in f.readlines()]\n
Run Code Online (Sandbox Code Playgroud)\n

类似地,应该是字节文字的字符串文字[101, 120, 97, 109, 112, 108, 101]只是缺少前缀:要使字节序列表示整数值(即字母的 ASCII 值example),请写入字节文字b\'example\',而不是字符串文字“\”例子\')。反之亦然。

\n

在原始示例的情况下,它看起来像:

\n
if b\'some-pattern\' in tmp:\n
Run Code Online (Sandbox Code Playgroud)\n

这里有一个内置的保护措施:字节文字语法仅允许 ASCII 字符,因此无论源文件的编码如何,类似的内容b\'\xc3\xabx\xc3\xa3m\xc3\xbel\xc3\xaa\'都会被捕获为SyntaxError,因为不清楚哪些字节值意味着;在旧版本中隐式编码方案中,ASCII 范围已经确定,但其他一切都悬而未决。)当然,bytes元素表示值 128..255 的文字仍然可以通过\\x对这些值使用转义来编写:例如,b\'\\xebx\\xe3m\\xfel\\xea\'将产生与\xc3\xabx\xc3\xa3m\xc3\xbel\xc3\xaaLatin-1 (ISO 8859-1) 编码中的文本相对应的字节序列。

\n

适当时进行转换

\n

仅当编码已确定时,才能在字节序列和文本之间进行转换。一直如此;我们过去只是在本地假设一种编码,然后大多忽略我们已经这样做了。(东亚等地的程序员在历史上更意识到这个问题,因为他们通常需要使用具有超过 256 个不同符号的脚本,因此他们的文本需要多字节编码。)

\n

在 3.x 中,由于没有压力能够将字节序列隐式地视为具有假定编码的文本,因此幕后没有隐式转换步骤。这意味着理解 API 很简单:字节是原始数据;因此,它们用于对文本进行编码,而文本是一种抽象。因此, (代表文本).encode()提供了方法str,以便将文本编码为原始数据。类似地,该.decode()方法由bytes(表示字节序列)提供,以便将原始数据解码为文本。

\n

将这些应用到示例代码,再次假设 UTF-8 编码合适,给出:

\n
if \'some-pattern\'.encode(\'utf-8\') in tmp:\n
Run Code Online (Sandbox Code Playgroud)\n

\n
if \'some-pattern\' in tmp.decode(\'utf-8\'):\n
Run Code Online (Sandbox Code Playgroud)\n