TypeError: 'str' 不支持缓冲区接口

Posted

技术标签:

【中文标题】TypeError: \'str\' 不支持缓冲区接口【英文标题】:TypeError: 'str' does not support the buffer interfaceTypeError: 'str' 不支持缓冲区接口 【发布时间】:2011-07-25 04:15:14 【问题描述】:
plaintext = input("Please enter the text you want to compress")
filename = input("Please enter the desired filename")
with gzip.open(filename + ".gz", "wb") as outfile:
    outfile.write(plaintext) 

上面的python代码给了我以下错误:

Traceback (most recent call last):
  File "C:/Users/Ankur Gupta/Desktop/Python_works/gzip_work1.py", line 33, in <module>
    compress_string()
  File "C:/Users/Ankur Gupta/Desktop/Python_works/gzip_work1.py", line 15, in compress_string
    outfile.write(plaintext)
  File "C:\Python32\lib\gzip.py", line 312, in write
    self.crc = zlib.crc32(data, self.crc) & 0xffffffff
TypeError: 'str' does not support the buffer interface

【问题讨论】:

@MikePennington:请解释为什么压缩文本没有用? 【参考方案1】:

从py2切换到py3时经常会出现这个问题。在 py2 中,plaintext 代表 stringbyte 数组 类型,它是类型灵活的,能够双向摆动。在py3中plaintext现在只是一个字符串,更加明确,而outfile在二进制模式下打开时outfile.write()方法实际上是一个字节数组 ,因此引发异常。将输入更改为plaintext.encode('utf-8') 以解决问题。如果这让您感到困扰,请继续阅读。

在 py2 中,declaration for file.write 使您看起来好像传入了一个字符串:file.write(str)。实际上你传递的是一个字节数组,你应该读到这样的声明:file.write(bytes)。如果你这样读,问题很简单,file.write(bytes) 需要一个 bytes 类型,并且在 py3 中从 str 中获取 bytes 你转换它:

py3>> outfile.write(plaintext.encode('utf-8'))

为什么 py2 文档声明 file.write 带了一个字符串?那么在 py2 中,声明的区别并不重要,因为:

py2>> str==bytes         #str and bytes aliased a single hybrid class in py2
True

py2 的 str-bytes 类具有方法/构造函数,使其在某些方面表现得像字符串类,而在其他方面表现得像字节数组类。方便file.write是不是?:

py2>> plaintext='my string literal'
py2>> type(plaintext)
str                              #is it a string or is it a byte array? it's both!

py2>> outfile.write(plaintext)   #can use plaintext as a byte array

为什么 py3 会破坏这个好系统?好吧,因为在 py2 中,基本的字符串函数不适用于世界其他地方。用非ASCII字符测量单词的长度?

py2>> len('¡no')        #length of string=3, length of UTF-8 byte array=4, since with variable len encoding the non-ASCII chars = 2-6 bytes
4                       #always gives bytes.len not str.len

一直以来,您一直以为您是在询问 py2 中字符串的 len,但实际上您是从编码中获取字节数组的长度。这种模棱两可是双重职责课程的根本问题。你实现了哪个版本的方法调用?

那么好消息是 py3 解决了这个问题。它解开了 strbytes 类。 str 类有类似字符串的方法,单独的 bytes 类有字节数组方法:

py3>> len('¡ok')       #string
3
py3>> len('¡ok'.encode('utf-8'))     #bytes
4

希望知道这一点有助于揭开问题的神秘面纱,并使迁移的痛苦更容易承受。

【讨论】:

【参考方案2】:

对于 Python 3.x,您可以通过以下方式将文本转换为原始字节:

bytes("my data", "encoding")

例如:

bytes("attack at dawn", "utf-8")

返回的对象将使用outfile.write

【讨论】:

【参考方案3】:

如果不明确转换为某种编码,则无法将 Python 3“字符串”序列化为字节。

outfile.write(plaintext.encode('utf-8'))

可能是你想要的。这也适用于 python 2.x 和 3.x。

【讨论】:

【参考方案4】:

这个问题有一个更简单的解决方案。

您只需将t 添加到模式,使其变为wt。这会导致 Python 将文件作为文本文件而不是二进制文件打开。然后一切都会正常工作。

完整的程序变成这样:

plaintext = input("Please enter the text you want to compress")
filename = input("Please enter the desired filename")
with gzip.open(filename + ".gz", "wt") as outfile:
    outfile.write(plaintext)

【讨论】:

python2 也可以吗?是否可以让代码在 python2 和 python3 上运行? 哇,你真棒!谢谢!让我投票给你。这应该是公认的答案:)) 添加“t”可能会产生副作用。在 Windows 上,编码为文本的文件会将换行符 ("\n") 转换为 CRLF ("\r\n")。【参考方案5】:

如果您使用 Python3x,那么 string 与 Python 2.x 的类型不同,您必须将其转换为字节(对其进行编码)。

plaintext = input("Please enter the text you want to compress")
filename = input("Please enter the desired filename")
with gzip.open(filename + ".gz", "wb") as outfile:
    outfile.write(bytes(plaintext, 'UTF-8'))

也不要使用像 stringfile 这样的变量名,因为它们是模块或函数的名称。

编辑@Tom

是的,非 ASCII 文本也被压缩/解压缩。我使用 UTF-8 编码的波兰字母:

plaintext = 'Polish text: ąćęłńóśźżĄĆĘŁŃÓŚŹŻ'
filename = 'foo.gz'
with gzip.open(filename, 'wb') as outfile:
    outfile.write(bytes(plaintext, 'UTF-8'))
with gzip.open(filename, 'r') as infile:
    outfile_content = infile.read().decode('UTF-8')
print(outfile_content)

【讨论】:

奇怪的是,这修复了它;原始代码在 3.1 下为我工作,文档中的示例代码也没有明确编码。如果在非 ASCII 文本上使用它,gunzip 会解压缩它吗?我遇到了一个错误。 我用 Unicode 印地语输入了我的名字,它成功地用 gzip 压缩了它。我正在使用 Python 3.2 @Tom Zych:可能与 3.2 的变化有关:docs.python.org/dev/whatsnew/3.2.html#gzip-and-zipfile 我使用 ActiveState Python 3.1 和 3.2 对其进行了测试。在我的机器上,两者都适用。 对于文件压缩,您应该始终以二进制模式打开输入:您需要能够稍后解压缩文件并获得完全相同的内容。转换为 Unicode (str) 并返回是不必要的,并且存在解码错误或输入和输出不匹配的风险。【参考方案6】:

对于django.test.TestCase 单元测试中的Django,我更改了我的Python2 语法:

def test_view(self):
    response = self.client.get(reverse('myview'))
    self.assertIn(str(self.obj.id), response.content)
    ...

使用 Python3 .decode('utf8') 语法:

def test_view(self):
    response = self.client.get(reverse('myview'))
    self.assertIn(str(self.obj.id), response.content.decode('utf8'))
    ...

【讨论】:

【参考方案7】:
>>> s = bytes("s","utf-8")
>>> print(s)
b's'
>>> s = s.decode("utf-8")
>>> print(s)
s

如果对你有用,以防删除烦人的“b”字符。如果有人有更好的想法,请建议我或随时在这里编辑我。我只是新手

【讨论】:

你也可以使用s.encode('utf-8')s.decode('utf-8') 那样pythonic 代替s = bytes("s", "utf-8")

以上是关于TypeError: 'str' 不支持缓冲区接口的主要内容,如果未能解决你的问题,请参考以下文章

TypeError:不支持的操作数类型/:'str'和'str'

TypeError:&:'str'和'str'不支持的操作数类型

Spark - TypeError:'str'和'int'的实例之间不支持'<='

TypeError: 不支持的操作数类型 -: 'datetime.date' 和 'str'

TypeError:不支持的操作数类型/:'str'和'str'django setting.py

如何解决 TypeError:'int' 和 'str' 的实例之间不支持'<'?