【问题标题】:TypeError: a bytes-like object is required, not 'str' when writing to a file in Python3TypeError:在 Python3 中写入文件时需要一个类似字节的对象,而不是“str”
【发布时间】:2016-01-08 08:41:36
【问题描述】:

我最近迁移到 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

升级到 3.5 后,我得到了:

TypeError: a bytes-like object is required, not 'str'

最后一行出错(模式搜索代码)。

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

if tmp.find('some-pattern') != -1: continue

-无济于事。

我能够快速解决几乎所有 2:3 的问题,但是这个小声明让我很烦。

【问题讨论】:

  • 为什么你以二进制模式打开文件却把它当作文本?
  • @MartijnPieters 感谢您发现文件打开模式!将其更改为文本模式解决了这个问题......尽管代码在 Py2k 中可靠地运行了很多年......
  • 我也遇到了这个问题,我有一个请求result = requests.get 并尝试x = result.content.split("\n")。我对错误消息有点困惑,因为它似乎暗示result.content 是一个字符串,而.split() 需要一个类似字节的对象..?? ("需要一个类似字节的对象,而不是 'str"')..
  • Martjin 是对的,您应该将'rb' 选项更改为'r' 以将文件视为字符串

标签: python python-3.x string file byte


【解决方案1】:

对于这个小例子:

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()

在前面加上“b” 'GET http://www.py4inf.com/code/romeo.txt HTTP/1.0\n\n' 解决了我的问题

【讨论】:

    【解决方案2】:

    当我尝试将字符(或字符串)转换为 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'\xc3\xb2'
    

    在我的情况下,我必须在编码字节时使用iso_8859_1 才能解决问题。

    希望这对某人有所帮助。

    【讨论】:

    • 请注意,文件顶部的 coding 注释不会影响 bytesencode 的工作方式,它只会改变 Python 源代码中字符的解释方式。
    【解决方案3】:

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

    例如:

    file.write(answers[i] + '\n'.encode())
    

    line.split(' +++$+++ '.encode())
    

    【讨论】:

      【解决方案4】:

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

      下面的代码会抛出 类型错误:需要一个类似字节的对象,而不是“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')
      

      【讨论】:

        【解决方案5】:

        您可以使用.encode() 对字符串进行编码

        例子:

        'Hello World'.encode()
        

        【讨论】:

        • 这条评论在使用fd.subprocess.Popen(); fd.communicate(...);的上下文中非常有用。
        【解决方案6】:

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

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

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

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

        【讨论】:

          【解决方案7】:

          你必须从 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'])
          

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

          来源:How to remove ^M

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

          【讨论】:

            【解决方案8】:

            就像已经提到的那样,您正在以二进制模式读取文件,然后创建一个字节列表。在您的以下 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'>
            

            【讨论】:

            • Python 2 确实有一种字节类型,只是混淆地称为str,而文本字符串的类型称为unicode。在 Python 3 中,他们更改了 str 的含义,使其与旧的 unicode 类型相同,并将旧的 str 重命名为 bytes。他们还删除了一堆会自动尝试从一种转换到另一种的情况。
            【解决方案9】:

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

            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'

            【讨论】:

            • 如果您查看 ppl 链接到的各种文档,您会发现 Py2 中的所有内容都“正常工作”,因为默认字符串是字节,而在 Py3 中,默认字符串是 Unicode,这意味着任何时候你正在做 I/O,尤其是。网络,字节字符串是标准,所以你必须学会​​移动黑白 Unicode 和字节字符串(en/decode)。对于文件,我们现在有“r”与“rb”(以及“w”和“a”)来帮助区分。
            • @wescpy:Python 2 有 'r''rb' too,在二进制文件和文本文件行为之间切换(如翻译换行符和在某些平台上,EOF 标记如何被处理)。 io 库(在 Python 3 中提供默认 I/O 功能,但在 Python 2 中也可用)现在也默认解码文本文件是真正的变化。
            • @MartijnPieters:是的,同意。在 2.x 中,我只在 DOS/Windows 上处理二进制文件时才使用'b' 标志(因为二进制是 POSIX 默认的)。在 3.x 中使用io 进行文件访问时,有双重目的是很好的。
            • @ericOnline ZipFile.open() docs explicitly state that only binary mode is supported二进制类文件对象的形式访问存档的成员)。您可以将文件对象包装在io.TextIOWrapper() 中以达到相同的效果。
            • @ericOnline 也不要使用.readlines(),当您可以直接遍历文件对象时。尤其是当您只需要来自单行的信息时。当可以在第一个缓冲块中找到该信息时,为什么要将所有内容都读入内存?
            猜你喜欢
            • 1970-01-01
            • 2017-08-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多