TypeError: 'str' arabellek arayüzünü desteklemiyor


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) 

Yukarıdaki python kodu bana aşağıdaki hatayı veriyor:

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: Lütfen metni sıkıştırmanın neden yararlı olmadığını açıklayın?
galinette

Yanıtlar:


295

Python3x kullanırsanız string, Python 2.x ile aynı tür değilse, baytlara dökmeniz gerekir (kodlayı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'))

Ayrıca , modül veya işlev adları gibi stringveya filebunlar gibi değişken adları kullanmayın .

@Tom'u DÜZENLE

Evet, ASCII olmayan metinler de sıkıştırılır / açılır. UTF-8 kodlamalı Lehçe harfleri kullanıyorum:

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)

Bunu düzeltmesi tuhaf; orijinal kod 3.1 altında benim için çalıştı ve belgelerdeki örnek kod da açıkça kodlamaz. ASCII olmayan metinde kullanırsanız, gunzip sıkıştırmayı açar mı? Bir hata aldım.
Tom Zych

Adımı Unicode Hintçe yazdım ve başarıyla gzip'te sıkıştırdı. Python 3.2 kullanıyorum
Gelecek Kral

@Tom Zych: Muhtemelen 3.2'deki değişikliklerle ilgili bir şey var: docs.python.org/dev/whatsnew/3.2.html#gzip-and-zipfile
Skurmedel

ActiveState Python 3.1 ve 3.2 ile test ettim. Makinemde her ikisinde de çalışıyor.
Michał Niklas

1
Dosya sıkıştırmak için girdiyi her zaman ikili modda açmalısınız: Dosyayı daha sonra açabilmeniz ve tam olarak aynı içeriği alabilmeniz gerekir. Unicode ( str) ve arkaya dönüştürmek gereksizdir ve giriş ve çıkış arasındaki kod çözme hataları veya uyumsuzlukları riski vardır.
alexis

96

Bu sorunun daha kolay bir çözümü var.

Sadece tmoda bir a eklemeniz gerekir, böylece olur wt. Bu, Python'un dosyayı ikili değil metin dosyası olarak açmasına neden olur. O zaman her şey işe yarayacak.

Programın tamamı şöyle olur:

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 üzerinde de çalışıyor mu? Kodun python2 ve python3 üzerinde çalışmasını sağlamanın bir yolu olabilir mi?
Loïc Faure-Lacroix

Vay canına, sen iyisin! Teşekkürler! Size oy vereyim. Bu kabul edilen cevap olmalı :))
Loïc

15
"T" eklemenin yan etkileri olabilir. Metin olarak kodlanan Windows dosyalarında, CRLF'ye ("\ r \ n") dönüştürülmüş yeni satırlar ("\ n") bulunur.
Bitwise

42

Python 3 'dizesini' bazı kodlamaya explict dönüştürme olmadan baytlara serileştiremezsiniz.

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

muhtemelen istediğiniz şeydir. Ayrıca bu hem python 2.x hem de 3.x için çalışır.


28

Python 3.x için metninizi ham bayta çevirebilirsiniz:

bytes("my data", "encoding")

Örneğin:

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

Döndürülen nesne ile çalışacaktır outfile.write.


9

Bu sorun genellikle py2'den py3'e geçerken ortaya çıkar. Py2'de plaintexthem bir dize hem de bir bayt dizi türüdür. PY3 olarak plaintextsadece bir dize ve yöntem outfile.write()aslında sürer bayt dizisi ne zaman outfilebir özel duruma neden yüzden ikili modunda açılır. plaintext.encode('utf-8')Sorunu gidermek için girişi olarak değiştirin . Bu sizi rahatsız ederse okumaya devam edin.

Py2 olarak, file.write için deklarasyon bir dize geçirilen gibi görünüyor yaptı: file.write(str). Aslında bir bayt dizide geçiyordu, böyle bildirimi okunurken olmalıydı: file.write(bytes). Eğer sorun basittir bunu böyle okursanız file.write(bytes)bir ihtiyacı bayt olsun türünü ve PY3 içinde bayt dışarı ait str bunu dönüştürmek:

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

Py2 belgeleri neden file.writeip aldığını açıkladı ? Py2'de beyan ayrımı önemli değildi çünkü:

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

Str-bayt py2 sınıf yöntemleri / o bazı yönlerden bir dize sınıf ve diğerlerinde bir bayt dizisi sınıf gibi davranmasını sağlayan oluşturucusu yok. Uygun mu 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 neden bu güzel sistemi kırdı? Peki çünkü py2'de temel dize fonksiyonları dünyanın geri kalanı için işe yaramadı. ASCII karakteri olmayan bir kelimenin uzunluğunu ölçmek ister misiniz?

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

Eğer soran sanıyordum Tüm bu zaman len py2 bir dize, kodlanmasını bayt dizinin uzunluğunu elde edildi. Bu belirsizlik, çift görevli sınıfların temel sorunudur. Herhangi bir yöntem çağrısının hangi sürümünü uyguluyorsunuz?

İyi haber, py3'ün bu sorunu çözmesidir. Str ve bytes sınıflarını çözer . Str sınıfı vardır tel benzeri bir yöntem, ayrı bayt sınıfı bayt dizisi yöntem vardır:

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

Umarım bunun bilinmesi meselenin gizemini ortadan kaldırır ve göç ağrısını taşımayı biraz kolaylaştırır.


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

Sinir bozucu 'b' karakterini kaldırmanız durumunda sizin için yararlıysa.


Ayrıca yerine s.encode('utf-8')bu kadar pythonic kullanabilirsinizs.decode('utf-8')s = bytes("s", "utf-8")
Hans Zimermann

4

İçin Djangode django.test.TestCasebirim testleri, benim değişti Python2 sözdizimi:

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

Python3 .decode('utf8') sözdizimini kullanmak için :

def test_view(self):
    response = self.client.get(reverse('myview'))
    self.assertIn(str(self.obj.id), response.content.decode('utf8'))
    ...
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.