TypeError: 'str' ไม่รองรับส่วนต่อประสานบัฟเฟอร์


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) 

รหัสหลามข้างต้นให้ฉันข้อผิดพลาดดังต่อไปนี้:

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: โปรดอธิบายว่าเพราะเหตุใดการบีบอัดข้อความจึงไม่มีประโยชน์
galinette

คำตอบ:


295

หากคุณใช้ 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'))

อย่าใช้ชื่อตัวแปรเช่นstringหรือfileในขณะที่ชื่อของโมดูลหรือฟังก์ชั่น

แก้ไข @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 จะขยายขนาดหรือไม่ ฉันพบข้อผิดพลาด
Tom Zych

ฉันพิมพ์ชื่อใน Unicode ภาษาฮินดีแล้วบีบอัดเป็น gzip สำเร็จ ฉันกำลังใช้ Python 3.2
Future King

@ Tom Zych: น่าจะมีสิ่งที่จะทำอย่างไรกับการเปลี่ยนแปลงใน 3.2: docs.python.org/dev/whatsnew/3.2.html#gzip-and-zipfile
Skurmedel

ฉันทดสอบด้วย ActiveState Python 3.1 และ 3.2 บนเครื่องของฉันมันใช้ได้ทั้งคู่
Michał Niklas

1
สำหรับการบีบอัดไฟล์ที่คุณควรเสมอเปิดการป้อนข้อมูลในโหมดไบนารี: คุณจำเป็นต้องมีความสามารถในการบีบอัดไฟล์ในภายหลังและได้รับสิ่งเนื้อหาเดียวกัน การแปลงเป็น Unicode ( str) และด้านหลังนั้นไม่จำเป็นและความเสี่ยงในการถอดรหัสข้อผิดพลาดหรือความไม่ตรงกันระหว่างอินพุตและเอาต์พุต
alexis

96

มีวิธีแก้ไขปัญหานี้ได้ง่ายขึ้น

คุณเพียงแค่เพิ่ม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 หรือไม่?
Loïc Faure-Lacroix

ว้าวคุณเป็นคนดี! ขอบคุณ! ให้ฉันโหวตให้คุณ นี้ควรจะเป็นคำตอบที่ได้รับการยอมรับ :))
Loïc

15
การเพิ่ม "t" สามารถมีผลข้างเคียง ในไฟล์ windows ที่เข้ารหัสเป็นข้อความจะมีการขึ้นบรรทัดใหม่ ("\ n") เป็น CRLF ("\ r \ n")
BitwiseMan

42

คุณไม่สามารถทำให้อนุกรม 'Python 3' เป็นไบต์โดยไม่ต้องแปลงการแปลงเป็นการเข้ารหัสบางส่วน

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

อาจเป็นสิ่งที่คุณต้องการ ใช้ได้ทั้ง python 2.x และ 3.x


28

สำหรับ Python 3.x คุณสามารถแปลงข้อความของคุณเป็นไบต์ดิบผ่าน:

bytes("my data", "encoding")

ตัวอย่างเช่น:

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

outfile.writeวัตถุที่ส่งกลับจะทำงานร่วมกับ


9

ปัญหานี้มักเกิดขึ้นเมื่อเปลี่ยนจาก py2 เป็น py3 ใน py2 plaintextเป็นทั้งสตริงและชนิดอาร์เรย์แบบไบต์ ใน py3 plaintextเป็นเพียงสตริงและวิธีการoutfile.write()ใช้อาร์เรย์ไบต์จริงเมื่อoutfileเปิดในโหมดไบนารีดังนั้นข้อยกเว้นจะเพิ่มขึ้น เปลี่ยนอินพุตเป็นplaintext.encode('utf-8')เพื่อแก้ไขปัญหา อ่านต่อถ้าเรื่องนี้รบกวนคุณ

ใน py2 ที่ประกาศสำหรับ file.writefile.write(str)ทำให้มันดูเหมือนคุณผ่านในสตริง: file.write(bytes)ที่จริงคุณได้ผ่านในอาร์เรย์ไบต์ที่คุณควรจะได้รับการอ่านประกาศเช่นนี้: ถ้าคุณอ่านแบบนี้ปัญหานี้ง่ายfile.write(bytes)ต้องใช้ชนิดไบต์และใน py3 เพื่อรับไบต์จากstr ที่คุณแปลง:

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

เหตุใด py2 docs file.writeจึงประกาศใช้สตริง ใน py2 ความแตกต่างของการประกาศไม่สำคัญเพราะ:

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

STR-ไบต์ชั้น py2 มีวิธีการ / ก่อสร้างที่ทำให้มันทำตัวเหมือนชั้นสตริงในบางวิธีและระดับไบต์อาร์เรย์ในคนอื่น ๆ สะดวก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

ตลอดเวลาที่คุณคิดว่าคุณกำลังขอให้lenของสตริงใน py2 คุณได้รับความยาวของอาร์เรย์ไบต์จากการเข้ารหัส ความกำกวมนั้นเป็นปัญหาพื้นฐานของการเรียนสองหน้าที่ คุณเรียกใช้เมธอดเวอร์ชันใด

ข่าวดีก็คือ py3 แก้ไขปัญหานี้ มันเลิกคลาสstrและไบต์ STRชั้นจะมีสตริงเหมือนวิธีการแยกไบต์ชั้นมีวิธีการอาร์เรย์ไบต์:

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

หวังว่าการรู้ว่าสิ่งนี้จะช่วยลดความน่าสงสัยของปัญหาและทำให้ความเจ็บปวดจากการย้ายถิ่นฐานง่ายขึ้นเล็กน้อย


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

ดีถ้ามีประโยชน์สำหรับคุณในกรณีที่ลบตัวอักษร 'b' ที่น่ารำคาญถ้าใครมีความคิดที่ดีกว่าโปรดแนะนำฉันหรืออย่าลังเลที่จะแก้ไขฉันได้ตลอดเวลาในที่นี่ฉันแค่มือใหม่


คุณสามารถใช้s.encode('utf-8')pythonic s.decode('utf-8')แทนได้s = bytes("s", "utf-8")
Hans Zimermann

4

สำหรับDjangoในdjango.test.TestCaseการทดสอบหน่วยฉันเปลี่ยนไวยากรณ์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'))
    ...
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.