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) 

上記の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

1
@MikePennington:テキストの圧縮が役に立たない理由を説明してください。
ガリネット

回答:


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一方で、それらのモジュールや関数の名前があります。

@トムを編集

はい、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
Skurmedel

ActiveState Python 3.1および3.2でテストしました。私のマシンでは両方で動作します。
のMichałニクラス

1
ファイル圧縮の場合は、常にバイナリモードで入力を開く必要があります。後でファイルを解凍して、まったく同じコンテンツを取得できる必要があります。Unicode(str)への変換およびその逆の変換は不要であり、デコードエラーまたは入力と出力の不一致のリスクがあります。
アレクシス

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ïc2015年

15
「t」を追加すると、副作用が生じる可能性があります。テキストとしてエンコードされたWindowsファイルでは、改行( "\ n")がCRLF( "\ r \ n")に変換されます。
BitwiseMan 2016年

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.writeの宣言により、文字列で渡されたように見えました:file.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

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がこの問題を修正することです。strクラスとbytesクラスのもつれを解きます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

以下の場合Djangodjango.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.