TypeError: 'str' không hỗ trợ giao diện bộ đệm


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

Mã python ở trên đang cho tôi lỗi sau:

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

1
@MikePennington: vui lòng giải thích tại sao nén văn bản không hữu ích?
galinette

Câu trả lời:


295

Nếu bạn sử dụng Python3x thì stringkhông cùng loại với Python 2.x, bạn phải chuyển nó thành byte (mã hóa nó).

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

Cũng không sử dụng tên biến như stringhoặcfile trong khi đó là tên của mô-đun hoặc chức năng.

EDIT @Tom

Có, văn bản không phải ASCII cũng được nén / giải nén. Tôi sử dụng các chữ cái Ba Lan với mã hóa 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)

Thật kỳ lạ khi điều này đã sửa nó; mã gốc làm việc cho tôi dưới 3.1 và mã mẫu trong tài liệu cũng không mã hóa rõ ràng. Nếu bạn sử dụng nó trên văn bản không phải ASCII, gunzip có giải nén được không? Tôi đã có một lỗi.
Tom Zych

Tôi đã gõ Tên của mình bằng Unicode Hindi và nó đã nén nó thành gzip. Tôi đang sử dụng Python 3.2
Vua tương lai

@ Tom Zych: Có lẽ có cái gì để làm với những thay đổi trong 3.2: docs.python.org/dev/whatsnew/3.2.html#gzip-and-zipfile
Skurmedel

Tôi đã thử nghiệm nó với ActiveState Python 3.1 và 3.2. Trên máy của tôi, nó hoạt động trong cả hai.
Michał Niklas

1
Để nén tệp, bạn phải luôn mở đầu vào ở chế độ nhị phân: Bạn cần có thể giải nén tệp sau đó và có được chính xác cùng một nội dung. Chuyển đổi sang Unicode ( str) và ngược lại là không cần thiết và có nguy cơ giải mã lỗi hoặc không khớp giữa đầu vào và đầu ra.
alexis

96

Có một giải pháp dễ dàng hơn cho vấn đề này.

Bạn chỉ cần thêm một tvào chế độ để nó trở thànhwt . Điều này khiến Python mở tệp dưới dạng tệp văn bản chứ không phải tệp nhị phân. Sau đó, mọi thứ sẽ chỉ hoạt động.

Chương trình hoàn chỉnh trở thành thế này:

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)

Nó cũng hoạt động trên python2? Nó có thể là một cách để làm cho mã hoạt động trên python2 và python3?
Loïc Faure-Lacroix

Wow, người đàn ông bạn tốt! Cảm ơn! Hãy để tôi bỏ phiếu cho bạn. Đây phải là câu trả lời được chấp nhận :))
Loïc

15
Thêm "t" có thể có tác dụng phụ. Trên các tệp windows được mã hóa dưới dạng văn bản sẽ có dòng mới ("\ n") được chuyển đổi thành CRLF ("\ r \ n").
BitwiseMan

42

Bạn không thể tuần tự hóa chuỗi '3' của Python thành byte mà không thể chuyển đổi sang mã hóa.

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

có thể là những gì bạn muốn. Ngoài ra, điều này hoạt động cho cả python 2.x và 3.x.


28

Đối với Python 3.x, bạn có thể chuyển đổi văn bản của mình thành byte thô thông qua:

bytes("my data", "encoding")

Ví dụ:

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

Các đối tượng trả lại sẽ làm việc với outfile.write.


9

Vấn đề này thường xảy ra khi chuyển từ py2 sang py3. Trong py2 plaintextcó cả kiểu chuỗi và kiểu byte . Trong py3 plaintextchỉ là một chuỗi và phương thức outfile.write()thực sự lấy một mảng byte khi outfileđược mở ở chế độ nhị phân, do đó, một ngoại lệ được đưa ra. Thay đổi đầu vào thànhplaintext.encode('utf-8') để khắc phục vấn đề. Đọc nếu điều này làm phiền bạn.

Trong py2, khai báo cho file.write làm cho có vẻ như bạn đã truyền vào một chuỗi : file.write(str). Trên thực tế bạn đã truyền vào một mảng byte, bạn nên đọc phần khai báo như thế này : file.write(bytes). Nếu bạn đọc nó như thế này thì vấn đề rất đơn giản, file.write(bytes)cần một loại byte và trong py3 để lấy byte ra khỏi str bạn chuyển đổi nó:

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

Tại sao các tài liệu py2 tuyên bố file.writelấy một chuỗi? Trong py2, sự phân biệt khai báo không thành vấn đề bởi vì:

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

Lớp str-byte của py2 có các phương thức / hàm tạo làm cho nó hoạt động giống như một lớp chuỗi theo một số cách và một lớp mảng byte theo các cách khác. Thuận tiện cho file.writenó không?:

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

Tại sao py3 phá vỡ hệ thống tốt đẹp này? Vâng, bởi vì trong các hàm chuỗi cơ bản py2 không hoạt động với phần còn lại của thế giới. Đo độ dài của một từ với một ký tự không phải 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

Tất cả thời gian này bạn nghĩ rằng bạn đang yêu cầu len của một chuỗi trong py2, bạn đã nhận được độ dài của mảng byte từ mã hóa. Sự mơ hồ đó là vấn đề cơ bản với các lớp nhiệm vụ kép. Phiên bản nào của cuộc gọi phương thức nào bạn thực hiện?

Tin tốt sau đó là py3 đã khắc phục vấn đề này. Nó loại bỏ các lớp strbyte . Lớp str có các phương thức giống như chuỗi, lớp byte riêng biệt có các phương thức mảng byte:

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

Hy vọng biết điều này sẽ giúp giải quyết vấn đề và làm cho nỗi đau di cư trở nên dễ dàng hơn một chút.


4
>>> s = bytes("s","utf-8")
>>> print(s)
b's'
>>> s = s.decode("utf-8")
>>> print(s)
s

Chà nếu hữu ích cho bạn trong trường hợp loại bỏ ký tự 'b' gây phiền nhiễu. Nếu có ai có ý tưởng tốt hơn xin vui lòng đề nghị tôi hoặc thoải mái chỉnh sửa tôi bất cứ lúc nào ở đây. Tôi chỉ là người mới


Bạn cũng có thể sử dụng s.encode('utf-8')nó để s.decode('utf-8')thay thếs = bytes("s", "utf-8")
Hans Zimermann

4

Đối với Djangotrong django.test.TestCasekiểm tra đơn vị, tôi đã thay đổi tôi python2 cú pháp:

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

Để sử dụng cú pháp Python3 .decode('utf8') :

def test_view(self):
    response = self.client.get(reverse('myview'))
    self.assertIn(str(self.obj.id), response.content.decode('utf8'))
    ...
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.