バイトを文字列に変換する


2310

このコードを使用して、外部プログラムから標準出力を取得しています。

>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]

communication()メソッドはバイトの配列を返します。

>>> command_stdout
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n'

ただし、出力を通常のPython文字列として処理したいと思います。このように印刷できるように:

>>> print(command_stdout)
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2

それがbinascii.b2a_qp()メソッドの目的だと思っていましたが、試したところ、同じバイト配列が再び取得されました。

>>> binascii.b2a_qp(command_stdout)
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n'

バイト値を文字列に戻すにはどうすればよいですか?つまり、手動で行う代わりに「バッテリー」を使用します。そして、私はそれがPython 3で問題ないようにしたいと思います。


47
なぜ動作しないのstr(text_bytes)ですか?これは私には奇妙に思えます。
チャーリーパーカー

13
@CharlieParker str(text_bytes)エンコーディングを指定できないため。text_bytesの内容によっては、text_bytes.decode('cp1250) ` はとは非常に異なる文字列になる可能性がありますtext_bytes.decode('utf-8')
クレイグアンダーソン

6
そのため、str関数はもう実際の文字列に変換されません。なんらかの理由でエンコーディングを明示的に言う必要があるのですが、その理由を読むのが面倒です。変換してutf-8、urコードが機能するかどうかを確認してください。例var = var.decode('utf-8')
チャーリーパーカー、

1
@CraigAnderson:unicode_text = str(bytestring, character_encoding)Python 3で期待どおりに動作します。テキストにデコードするのではなく、テキスト表現を生成するunicode_text = bytestring.decode(character_encoding)だけstr(bytes_obj)で混乱を避ける方が望ましいbytes_objです。str(b'\xb6', 'cp1252') == b'\xb6'.decode('cp1252') == '¶'そしてstr(b'\xb6') == "b'\\xb6'" == repr(b'\xb6') != '¶'
jfs

回答:


3676

文字列を生成するには、bytesオブジェクトをデコードする必要があります。

>>> b"abcde"
b'abcde'

# utf-8 is used here because it is a very common encoding, but you
# need to use the encoding your data is actually in.
>>> b"abcde".decode("utf-8") 
'abcde'

58
使用"windows-1252"も信頼できない(たとえば、Windowsの他の言語バージョンの場合)sys.stdout.encoding。使用するのが最善ではないでしょうか。
nikow

12
多分これは誰かをさらに助けるでしょう:時々あなたは元TCP通信にバイト配列を使用します。バイト配列を文字列に変換して、末尾の '\ x00'文字を切り捨てる場合は、次の答えでは不十分です。次に、b'example \ x00 \ x00'.decode( 'utf-8')。strip( '\ x00')を使用します。
Wookie88 2013

2
バグについては、bugs.python.org / issue17860に文書化することでバグを埋めました。パッチをご提案ください。貢献することが難しい場合-コメントを改善する方法を歓迎します。
anatly techtonik 2013

44
Python 2.7.6ではb"\x80\x02\x03".decode("utf-8")->を処理しませんUnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 0: invalid start byte
martineau 2014年

9
コンテンツがランダムなバイナリ値である場合、utf-8変換は失敗する可能性があります。代わりに(下)@techtonikの答えを参照stackoverflow.com/a/27527728/198536
wallyk

215

バイト文字列をデコードして、文字(Unicode)文字列に変換する必要があります。

Python 2の場合

encoding = 'utf-8'
'hello'.decode(encoding)

または

unicode('hello', encoding)

Python 3の場合

encoding = 'utf-8'
b'hello'.decode(encoding)

または

str(b'hello', encoding)

2
Python 3では、文字列が変数にある場合はどうなりますか?
Alaa M.

1
@AlaaM .:同じです。あなたが持っている場合はvariable = b'hello'、その後、unicode_text = variable.decode(character_encoding)
JFS

182

この方法は簡単だと思います:

>>> bytes_data = [112, 52, 52]
>>> "".join(map(chr, bytes_data))
'p44'

6
ありがとう、あなたの方法は他の人がしなかったときに私のために働きました。文字列に変換する必要があるエンコードされていないバイト配列がありました。それを文字列にデコードできるように再エンコードする方法を見つけようとしていました。この方法は完璧に機能します!
leetNightshade 2014年

5
@leetNightshade:それでも、それはひどく非効率的です。バイト配列がある場合は、デコードするだけで済みます。
Martijn Pieters

12
@Martijnピータース私は、複数の万回の実行が実行して、これらの他の答えと簡単なベンチマークをしたstackoverflow.com/a/3646405/353094し、上記溶液は、はるかに高速実際にひとつひとつの時間でした。Python 2.7.7での10,000回の実行では8msかかりますが、他の12msおよび18msで実行されます。入力、Pythonのバージョンなどに応じて、多少の変動が生じる可能性があることは承知しています。私には遅すぎるようには見えません。
leetNightshade 14

5
@Martijn Pietersはい。したがって、その点では、これは、質問された質問の本文に対する最良の回答ではありません。そして、タイトルは誤解を招きやすいですね。バイト配列を文字列に変換するのではなく、バイト文字列を通常の文字列に変換したいと考えています。この回答は、質問された質問のタイトルには問題ありません。
leetNightshade 2014

5
python 3の場合、これは次と同等である必要がありbytes([112, 52, 52])ます
。p3

92

エンコーディングがわからない場合、バイナリ入力をPython 3およびPython 2互換の方法で文字列に読み込むには、古代のMS-DOS CP437エンコーディングを使用します。

PY3K = sys.version_info >= (3, 0)

lines = []
for line in stream:
    if not PY3K:
        lines.append(line)
    else:
        lines.append(line.decode('cp437'))

エンコーディングは不明であるため、英語以外の記号がの文字に変換されることを期待してくださいcp437(英語の文字は、ほとんどのシングルバイトエンコーディングとUTF-8で一致するため、変換されません)。

これを取得する可能性があるため、任意のバイナリ入力をUTF-8にデコードすることは安全ではありません。

>>> b'\x00\x01\xffsd'.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 2: invalid
start byte

同じことがlatin-1Python 2で一般的だった(デフォルトですか?)にも当てはまります。コードページレイアウトの不足しているポイントを参照してください。Pythonが悪名高いで窒息する場所ですordinal not in range

UPDATE 20150604:Python 3がsurrogateescapeデータを損失やクラッシュなしにバイナリデータにエンコードするエラー戦略を持っているという噂がありますが[binary] -> [str] -> [binary]、パフォーマンスと信頼性の両方を検証するには変換テストが必要です。

UPDATE 20170116Nearooのコメントのおかげで、backslashreplaceエラーハンドラーですべての不明なバイトをスラッシュエスケープする可能性もあります。これはPython 3でのみ機能するため、この回避策を使用しても、異なるバージョンのPythonから一貫性のない出力が得られます。

PY3K = sys.version_info >= (3, 0)

lines = []
for line in stream:
    if not PY3K:
        lines.append(line)
    else:
        lines.append(line.decode('utf-8', 'backslashreplace'))

詳細については、PythonのUnicodeサポートを参照してください。

UPDATE 20170119:Python 2とPython 3の両方で機能するスラッシュエスケープデコードを実装することにしました。これはcp437ソリューションよりも遅くなるはずですが、すべてのPythonバージョンで同じ結果を生成するはずです

# --- preparation

import codecs

def slashescape(err):
    """ codecs error handler. err is UnicodeDecode instance. return
    a tuple with a replacement for the unencodable part of the input
    and a position where encoding should continue"""
    #print err, dir(err), err.start, err.end, err.object[:err.start]
    thebyte = err.object[err.start:err.end]
    repl = u'\\x'+hex(ord(thebyte))[2:]
    return (repl, err.end)

codecs.register_error('slashescape', slashescape)

# --- processing

stream = [b'\x80abc']

lines = []
for line in stream:
    lines.append(line.decode('utf-8', 'slashescape'))

6
Pythonは欠落しているシンボルを置き換えて続行するメカニズムを提供するべきだと私は本当に感じています。
anatly techtonik 2015

@techtonik:これはpython2で機能したような配列では機能しません。
user2284570

@ user2284570リストのことですか?そして、なぜそれは配列で動作するはずですか?特にフロートの配列..
anatoly techtonik '22 / 10/22

また、単にのUnicodeエラーを無視することができますb'\x00\x01\xffsd'.decode('utf-8', 'ignore')のpython 3に
アントニスカルー

3
@anatolytechtonik文字列にエスケープシーケンスを残して次に進む可能性があります。b'\x80abc'.decode("utf-8", "backslashreplace")結果はになり'\\x80abc'ます。この情報は、この回答の執筆以降に更新されたと思われるUnicodeドキュメントページから取得されました。
Nearoo

86

Python 3では、デフォルトのエンコーディングは"utf-8"なので、直接使用できます。

b'hello'.decode()

これは

b'hello'.decode(encoding="utf-8")

一方、Python 2では、エンコーディングはデフォルトでデフォルトの文字列エンコーディングになります。したがって、以下を使用する必要があります。

b'hello'.decode(encoding)

encoding必要なエンコーディングはどこですか。

注:キーワード引数のサポートはPython 2.7で追加されました。


41

私はあなたが実際にこれを望んでいると思います:

>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
>>> command_text = command_stdout.decode(encoding='windows-1252')

Aaronの答えは正しかったですが、使用するエンコーディングを知っおく必要があります。そして、Windowsは「windows-1252」を使用していると思います。コンテンツに珍しい(非ASCII)文字が含まれている場合にのみ問題になりますが、違いが生じます。

ところで、それは事実ません、あなたがそれを指示しない限り、それはエンコーディングを知らないので、それは、それらの間の魔法に変換することはできません。問題は、Pythonはバイナリとテキストデータのための2つの異なる種類を使用して移動している理由です!あなたが知る唯一の方法は、Windowsのドキュメントを読む(またはここで読む)ことです。


3
open()テキストストリーム用の関数、またはPopen()渡す場合universal_newlines=Trueは、魔法のように文字エンコーディングを決定します(locale.getpreferredencoding(False)Python 3.3以降)。
jfs 2014

2
'latin-1'すべてのコードポイントが設定された逐語的エンコーディングであるため、これを使用して、Pythonがサポートする任意のタイプの文字列にバイト文字列を効果的に読み取ることができます(Python 2では逐語的、Python 3ではUnicodeに)。
tripleee 2017

@tripleee:mojibake 'latin-1'を取得する良い方法です。また、Windowsには魔法のような置換があります。あるプロセスから変更されていない別のプロセスにデータをパイプ処理するのは驚くほど困難です。例dir::\xb6> \x14(私の回答の最後の例)
jfs

32

universal_newlinesをTrueに設定します。

command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]

5
私はこの方法を使用してきましたが、うまくいきます。ただし、システムのユーザー設定に基づいてエンコーディングを推測しているだけなので、他のいくつかのオプションほど堅牢ではありません。これは、docs.python.org / 3.4 / library / subprocess.htmlを参照して、次のように動作します。 .getpreferredencoding(False)。」
twasbrillig 2014年

3.7text=Trueでは、の代わりに行うことができます(すべきです)universal_newlines=True
ボリス

23

一方で@Aaron Maenpaaの答えはただ働き、ユーザーが最近尋ねました

もっと簡単な方法はありますか?'fhand.read()。decode( "ASCII")' [...]とても長いです!

以下を使用できます。

command_stdout.decode()

decode()持っている標準の引数を

codecs.decode(obj, encoding='utf-8', errors='strict')


.decode()を使用'utf-8'すると失敗する可能性があります(コマンドの出力で別の文字エンコーディングを使用したり、デコードできないバイトシーケンスを返すこともあります)ただし、入力がascii(utf-8のサブセット)の場合は.decode()機能します。
jfs

23

バイトシーケンスをテキストとして解釈するには、対応する文字エンコーディングを知っている必要があります。

unicode_text = bytestring.decode(character_encoding)

例:

>>> b'\xc2\xb5'.decode('utf-8')
'µ'

lsコマンドは、テキストとして解釈できない出力を生成する場合があります。Unixのファイル名は、スラッシュb'/'とゼロ以外の任意のバイトシーケンスにすることができます b'\0'

>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()

レイズをコードUTF-8を使用してこのようなバイトスープを復号しようUnicodeDecodeError

それは悪化する可能性があります。 互換性のない間違ったエンコーディングを使用すると、デコードがサイレントに失敗し、mojibakeが生成される場合があります。

>>> '—'.encode('utf-8').decode('cp1252')
'—'

データは破損していますが、プログラムは障害が発生したことを認識していません。

一般に、使用する文字エンコーディングはバイトシーケンス自体に埋め込まれていません。この情報は帯域外で伝達する必要があります。一部の結果は他の結果より可能性が高いため、文字エンコードを推測chardetできるモジュールが存在します。単一のPythonスクリプトは、異なる場所で複数の文字エンコーディングを使用する場合があります。


ls出力はos.fsdecode()デコードできないファイル名でも成功する関数を使用してPython文字列に変換できます(UNIXではエラーハンドラーを使用 sys.getfilesystemencoding()surrogateescapeます)。

import os
import subprocess

output = os.fsdecode(subprocess.check_output('ls'))

元のバイトを取得するには、を使用できますos.fsencode()

universal_newlines=Trueパラメータを渡すと、がバイトのデコードにsubprocess使用 locale.getpreferredencoding(False)cp1252れます。たとえば、Windows上にある場合があり ます。

オンザフライでバイトストリームをデコードするには、 例をio.TextIOWrapper() 使用できます。

コマンドによって、出力に異なる文字エンコーディングが使用される場合があります。たとえば、dir内部コマンド(cmd)でcp437が使用される場合があります。出力をデコードするには、エンコーディングを明示的に渡すことができます(Python 3.6+):

output = subprocess.check_output('dir', shell=True, encoding='cp437')

ファイル名はos.listdir()(Windows Unicode APIを使用する)とは異なる場合があります。たとえば、Pythonのcp437コーデックマップ'\xb6'で置き換えて、U + 00B6(¶)の代わりに文字U + 0014を制御できます。任意のUnicode文字を含むファイル名をサポートするには、ASCII以外のUnicode文字を含む可能性があるPowerShell出力をPython文字列にデコードするをご覧ください。 '\x14'b'\x14'


16

この質問は実際にsubprocess出力について尋ねているのでPopen、(Python 3.6+で)エンコーディングキーワードを受け入れるので、より直接的なアプローチを利用できます。

>>> from subprocess import Popen, PIPE
>>> text = Popen(['ls', '-l'], stdout=PIPE, encoding='utf-8').communicate()[0]
>>> type(text)
str
>>> print(text)
total 0
-rw-r--r-- 1 wim badger 0 May 31 12:45 some_file.txt

他のユーザーの一般的な答えは、バイトをテキストにデコードすることです:

>>> b'abcde'.decode()
'abcde'

引数なしsys.getdefaultencoding()で使用されます。データがでない場合sys.getdefaultencoding()は、decode呼び出しでエンコードを明示的に指定する必要があります。

>>> b'caf\xe9'.decode('cp1250')
'café'

3
または、Python 3.7 text=Trueでは、指定されたエンコーディング(設定されている場合)またはシステムのデフォルトを使用して、stdin、stdout、およびstderrをデコードするために渡すことができます。Popen(['ls', '-l'], stdout=PIPE, text=True)
ボリス

エンコーディングlsを使用した出力のデコードutf-8が失敗する場合があります(2016年の私の回答の例を参照)。
jfs

1
@Boris:encodingパラメータが指定されている場合、そのtextパラメータは無視されます。
jfs

11

あなたがしようとすることによって以下を取得する必要がある場合decode()

AttributeError: 'str'オブジェクトに属性 'decode'がありません

キャストで直接エンコードタイプを指定することもできます。

>>> my_byte_str
b'Hello World'

>>> str(my_byte_str, 'utf-8')
'Hello World'

6

Windowsシステムのデータ(\r\n行末を含む)を使用する場合、私の答えは

String = Bytes.decode("utf-8").replace("\r\n", "\n")

どうして?複数行のInput.txtでこれを試してください:

Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8")
open("Output.txt", "w").write(String)

すべての行末が2倍(から\r\r\n)になり、余分な空行になります。Pythonのテキスト読み取り関数は通常、文字列のみを使用するように行末を正規化します\n。Windowsシステムからバイナリデータを受け取った場合、Pythonはそれを行う機会がありません。したがって、

Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8").replace("\r\n", "\n")
open("Output.txt", "w").write(String)

元のファイルを複製します。


.replace("\r\n", "\n")こんなに長く探していました。これは、HTMLを適切にレンダリングしたい場合の答えです。
mhlavacka

5

リストを整理する機能を作りました

def cleanLists(self, lista):
    lista = [x.strip() for x in lista]
    lista = [x.replace('\n', '') for x in lista]
    lista = [x.replace('\b', '') for x in lista]
    lista = [x.encode('utf8') for x in lista]
    lista = [x.decode('utf8') for x in lista]

    return lista

6
あなたは、実際の全ての連鎖することができ.strip.replace.encode代わりにそれの上に5回の反復の後にリストを反復処理などのコール1つのリスト内包内のみを。
テイラーエドミストン2017年

1
@TaylorEdmistonおそらく割り当てを節約できますが、操作の数は同じままです。
JulienD 2017

5

Pythonの3のために、これは非常に安全であるPython的に変換するためのアプローチbyteにはstring

def byte_to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes): # Check if it's in bytes
        print(bytes_or_str.decode('utf-8'))
    else:
        print("Object not of byte type")

byte_to_str(b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2\n')

出力:

total 0
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar  3 07:03 file2

5
1)@bodanglyが言ったように、型チェックはPythonicではありません。2)あなたが書いた関数は、「命名されbyte_to_str、それはSTRを返します意味している」、それだけで変換された値を出力し、そしてそれが失敗した場合はエラーメッセージを出力します(ただし)例外は発生しません。このアプローチもPythonを使わず、bytes.decode提供したソリューションを難読化します。
cosmicFluke

3

sysから—システム固有のパラメータと関数

標準ストリームとの間でバイナリデータを読み書きするには、基になるバイナリバッファを使用します。たとえば、バイトをstdoutに書き込むには、を使用しますsys.stdout.buffer.write(b'abc')


3
サブプロセスへのパイプはすでにバイナリバッファです。あなたの答えは、結果のbytes値から文字列値を取得する方法に対処できません。
Martijn Pieters

1
def toString(string):    
    try:
        return v.decode("utf-8")
    except ValueError:
        return string

b = b'97.080.500'
s = '97.080.500'
print(toString(b))
print(toString(s))

1
このコードは質問に答えることがありますが、問題を解決する方法理由に関する追加のコンテキストを提供すると、回答の長期的な価値が向上します。あなたが今尋ねている人だけでなく、あなたが将来の読者のための質問に答えていることを忘れないでください!回答を編集して説明を追加し、適用される制限と前提を示してください。また、この回答が他の回答よりも適切である理由について言及しても問題ありません。
Dev-iL 2018年

説明が正しいでしょう。
Peter Mortensen

1

あなたのために特定の「シェルコマンドを実行して、バイトではなくテキストとして、その出力を得る」の場合、Pythonの3.7で、あなたが使用する必要があるsubprocess.runと渡すtext=True(同様のcapture_output=True出力をキャプチャします)

command_result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
command_result.stdout  # is a `str` containing your program's stdout

text以前はと呼ばれていましたがuniversal_newlines、Python 3.7で変更されました(まあ、エイリアス化されています)。3.7より前のバージョンのPythonをサポートする場合は、universal_newlines=True代わりにtext=True


0

バイトに変換された文字列だけでなく、バ​​イトを変換したい場合:

with open("bytesfile", "rb") as infile:
    str = base64.b85encode(imageFile.read())

with open("bytesfile", "rb") as infile:
    str2 = json.dumps(list(infile.read()))

ただし、これはあまり効率的ではありません。2 MBの画像を9 MBに変換します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.