I\'m using this code to get standard output from an external program:
>>> from subprocess import *
>>> command_stdout = Popen([\'ls\', \'-l
To interpret a byte sequence as a text, you have to know the corresponding character encoding:
unicode_text = bytestring.decode(character_encoding)
Example:
>>> b'\xc2\xb5'.decode('utf-8')
'µ'
ls
command may produce output that can't be interpreted as text. File names
on Unix may be any sequence of bytes except slash b'/'
and zero
b'\0'
:
>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()
Trying to decode such byte soup using utf-8 encoding raises UnicodeDecodeError
.
It can be worse. The decoding may fail silently and produce mojibake if you use a wrong incompatible encoding:
>>> '—'.encode('utf-8').decode('cp1252')
'—'
The data is corrupted but your program remains unaware that a failure has occurred.
In general, what character encoding to use is not embedded in the byte sequence itself. You have to communicate this info out-of-band. Some outcomes are more likely than others and therefore chardet
module exists that can guess the character encoding. A single Python script may use multiple character encodings in different places.
ls
output can be converted to a Python string using os.fsdecode()
function that succeeds even for undecodable
filenames (it uses
sys.getfilesystemencoding()
and surrogateescape
error handler on
Unix):
import os
import subprocess
output = os.fsdecode(subprocess.check_output('ls'))
To get the original bytes, you could use os.fsencode()
.
If you pass universal_newlines=True
parameter then subprocess
uses
locale.getpreferredencoding(False)
to decode bytes e.g., it can be
cp1252
on Windows.
To decode the byte stream on-the-fly, io.TextIOWrapper() could be used: example.
Different commands may use different character encodings for their
output e.g., dir
internal command (cmd
) may use cp437. To decode its
output, you could pass the encoding explicitly (Python 3.6+):
output = subprocess.check_output('dir', shell=True, encoding='cp437')
The filenames may differ from os.listdir()
(which uses Windows
Unicode API) e.g., '\xb6'
can be substituted with '\x14'
—Python's
cp437 codec maps b'\x14'
to control character U+0014 instead of
U+00B6 (¶). To support filenames with arbitrary Unicode characters, see Decode PowerShell output possibly containing non-ASCII Unicode characters into a Python string