J'utilise ce code pour obtenir la sortie standard d'un programme externe:
>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
La méthode communic () renvoie un tableau d'octets:
>>> 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'
Cependant, j'aimerais utiliser la sortie comme une chaîne normale Python. Pour que je puisse l'imprimer comme ceci:
>>> 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
Je pensais que c'était le but de la méthode binascii.b2a_qp () , mais quand je l'ai essayée, j'ai à nouveau obtenu le même tableau d'octets:
>>> 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'
Est-ce que quelqu'un sait comment reconvertir la valeur en octets en chaîne? Je veux dire, utiliser les "piles" au lieu de le faire manuellement. Et j'aimerais que tout se passe bien avec Python 3.
Vous devez décoder l'objet bytes pour produire une chaîne:
>>> 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'
Vous devez décoder la chaîne d'octets et la transformer en chaîne de caractères (unicode).
Sur Python 2
encoding = 'utf-8'
'hello'.decode(encoding)
ou
unicode('hello', encoding)
Sur Python 3
encoding = 'utf-8'
b'hello'.decode(encoding)
ou
str(b'hello', encoding)
Je pense que cette façon est facile:
bytes_data = [112, 52, 52]
"".join(map(chr, bytes_data))
>> p44
Si vous ne connaissez pas l'encodage, utilisez les méthodes compatibles Python 3 et Python 2 pour lire les entrées binaires d'une chaîne, utilisez l'ancien MS-DOS cp437 codage:
PY3K = sys.version_info >= (3, 0)
lines = []
for line in stream:
if not PY3K:
lines.append(line)
else:
lines.append(line.decode('cp437'))
Comme l'encodage est inconnu, attendez-vous à ce que les symboles non anglais se traduisent en caractères de cp437
(les caractères anglais ne sont pas traduits, car ils correspondent à la plupart des encodages à un octet et UTF-8).
Décoder une entrée binaire arbitraire en UTF-8 est dangereux, car vous pourriez obtenir ceci:
>>> 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
La même chose s'applique à latin-1
, qui était populaire (par défaut?) Pour Python 2. Voir les points manquants dans Codepage Layout - c'est là que Python _ étrangle avec le tristement célèbre ordinal not in range
.
UPDATE 20150604 : Il y a des rumeurs selon lesquelles Python 3 a la stratégie d'erreur surrogateescape
permettant de coder des éléments dans des données binaires sans perte de données. et se bloque, mais il nécessite des tests de conversion [binary] -> [str] -> [binary]
pour valider les performances et la fiabilité.
UPDATE 20170116 : Grâce au commentaire de Nearoo, il est également possible de supprimer tous les octets inconnus avec le gestionnaire d'erreurs backslashreplace
. Cela ne fonctionne que pour Python 3. Ainsi, même avec cette solution de contournement, vous obtiendrez toujours une sortie incohérente de différentes versions de 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'))
Voir https://docs.python.org/3/howto/unicode.html#python-s-unicode-support pour plus de détails.
UPDATE 20170119 : J'ai décidé d'implémenter le décodage d'échappement slash qui fonctionne à la fois pour Python 2 et Python 3. La solution cp437
devrait être plus lente, mais elle devrait produire des résultats identiques sur chaque version 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'))
En Python , le codage par défaut est "utf-8"
, vous pouvez donc utiliser directement:
b'hello'.decode()
ce qui équivaut à
b'hello'.decode(encoding="utf-8")
Par contre, en Python 2 , le codage par défaut correspond au codage par défaut de la chaîne. Ainsi, vous devriez utiliser:
b'hello'.decode(encoding)
où encoding
est l'encodage souhaité.
Remarque: le support des arguments de mots clés a été ajouté dans Python 2.7.
Je pense que ce que vous voulez réellement, c'est ceci:
>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
>>> command_text = command_stdout.decode(encoding='windows-1252')
La réponse d'Aaron était correcte, sauf que vous avez besoin de savoir quel encodage utiliser. Et je crois que Windows utilise 'windows-1252'. Cela n'aura d'importance que si vous avez des caractères inhabituels (non-ASCII) dans votre contenu, mais alors cela fera une différence.
Soit dit en passant, c’est la raison pour laquelle Python a décidé d’utiliser deux types différents pour les données binaires et textuelles: il ne peut pas convertir magiquement entre eux car il ne connaît pas l’encodage, sauf si vous le dites! La seule façon que VOUS sachiez est de lire la documentation Windows (ou de la lire ici).
Définissez universal_newlines sur True, c.-à-d.
command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
Alors que la réponse de @ Aaron Maenpaa fonctionne, un utilisateur récemment demandé :
Y a-t-il plus simplement moyen? 'fhand.read (). decode ("ASCII")' [...] C'est si long!
Vous pouvez utiliser:
command_stdout.decode()
decode()
a un argument standard :
codecs.decode(obj, encoding='utf-8', errors='strict')
Puisque cette question concerne en fait la sortie de subprocess
, vous avez une approche plus directe disponible puisque Popen
accepte un codage mot-clé (en 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
La réponse générale pour les autres utilisateurs est de décoder octets en texte:
>>> b'abcde'.decode()
'abcde'
Sans argument, sys.getdefaultencoding()
sera utilisé. Si vos données ne sont pas sys.getdefaultencoding()
, vous devez spécifier explicitement le codage dans l'appel decode
:
>>> b'caf\xe9'.decode('cp1250')
'café'
Pour interpréter une séquence d'octets en tant que texte, vous devez connaître le codage de caractères correspondant:
unicode_text = bytestring.decode(character_encoding)
Exemple:
>>> b'\xc2\xb5'.decode('utf-8')
'µ'
La commande ls
peut produire une sortie qui ne peut pas être interprétée comme du texte. Les noms de fichiers sous Unix peuvent être n’importe quelle séquence d’octets à l’exception de la barre oblique b'/'
et de zéro b'\0'
:
>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()
Essayer de décoder un octet pareil en utilisant le codage utf-8 soulève UnicodeDecodeError
.
Cela peut être pire. Le décodage peut échouer en silence et produire mojibake si vous utilisez un encodage incompatible incorrect:
>>> '—'.encode('utf-8').decode('cp1252')
'—'
Les données sont corrompues mais votre programme ne sait toujours pas qu’un échec s’est produit.
En général, le codage de caractères à utiliser n'est pas intégré à la séquence d'octets elle-même. Vous devez communiquer cette information hors bande. Certains résultats sont plus probables que d'autres et donc le module chardet
existe qui peut deviner le codage des caractères. Un seul script Python peut utiliser plusieurs codages de caractères à différents endroits.
La sortie ls
peut être convertie en une chaîne Python à l'aide de la fonction os.fsdecode()
qui réussit même pour noms de fichiers indécodables (il utilise sys.getfilesystemencoding()
et surrogateescape
gestionnaire d'erreurs sur Unix) :
import os
import subprocess
output = os.fsdecode(subprocess.check_output('ls'))
Pour obtenir les octets d'origine, vous pouvez utiliser os.fsencode()
.
Si vous transmettez le paramètre universal_newlines=True
, alors subprocess
utilise locale.getpreferredencoding(False)
pour décoder les octets, par exemple. Il peut s'agir de cp1252
sous Windows.
Pour décoder le flux d'octets à la volée, io.TextIOWrapper()
peut être utilisé: exemple .
Différentes commandes peuvent utiliser différents codages de caractères pour leur sortie, par exemple, dir
commande interne (cmd
) peut utiliser cp437. Pour décoder sa sortie, vous pouvez transmettre explicitement le codage (Python 3.6+):
output = subprocess.check_output('dir', Shell=True, encoding='cp437')
Les noms de fichiers peuvent différer de os.listdir()
(qui utilise l'API Windows Unicode), par exemple, '\xb6'
peut être remplacé par '\x14'
— Les codecs cp437 de Python b'\x14'
pour contrôler le caractère U + 0014 au lieu de U + 00B6. (¶) Pour prendre en charge les noms de fichiers avec des caractères Unicode arbitraires, voir La sortie poweshell de Decode peut contenir des caractères Unicode non ascii dans une chaîne python
Si vous deviez obtenir ce qui suit en essayant decode()
:
AttributeError: l'objet 'str' n'a pas d'attribut 'decode'
Vous pouvez également spécifier le type de codage directement dans une distribution:
>>> my_byte_str
b'Hello World'
>>> str(my_byte_str, 'utf-8')
'Hello World'
Lorsque je travaille avec des données de systèmes Windows (avec \r\n
fin de ligne), ma réponse est
String = Bytes.decode("utf-8").replace("\r\n", "\n")
Pourquoi? Essayez ceci avec un fichier multiligne Input.txt:
Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8")
open("Output.txt", "w").write(String)
Toutes les fins de ligne seront doublées (jusqu'à \r\r\n
), ce qui entraînera des lignes vides supplémentaires. Les fonctions de lecture de texte de Python normalisent généralement les fins de ligne afin que les chaînes n'utilisent que \n
. Si vous recevez des données binaires d'un système Windows, Python n'a pas la possibilité de le faire. Ainsi,
Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8").replace("\r\n", "\n")
open("Output.txt", "w").write(String)
va répliquer votre fichier d'origine.
J'ai créé une fonction pour nettoyer une liste
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
Pour Python 3, il s'agit d'une approche beaucoup plus sûre et Pythonic pour convertir de byte
à string
:
def byte_to_str(bytes_or_str):
if isinstance(bytes_or_str, bytes): #check if its 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')
Sortie:
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
De http://docs.python.org/3/library/sys.html ,
Pour écrire ou lire des données binaires depuis/vers les flux standard, utilisez le tampon binaire sous-jacent. Par exemple, pour écrire des octets sur la sortie standard, utilisez sys.stdout.buffer.write(b'abc')
.
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))
Si vous voulez convertir des octets, pas seulement une chaîne convertie en octets:
with open("bytesfile", "rb") as infile:
str = base64.b85encode(imageFile.read())
with open("bytesfile", "rb") as infile:
str2 = json.dumps(list(infile.read()))
Ce n'est pas très efficace, cependant. Il transformera une image de 2 mb en 9 mb.
Pour votre cas spécifique de "exécuter une commande Shell et obtenir sa sortie sous forme de texte au lieu d'octets", sur Python 3.7, vous doit utiliser subprocess.run
et transmettre _text=True
_ (ainsi que _capture_output=True
_ pour capturer le résultat)
_command_result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
command_result.stdout # is a `str` containing your program's stdout
_
text
s'appelait _universal_newlines
_ et a été changé (bien, aliasé) dans Python 3.7. Si vous souhaitez prendre en charge les versions de Python antérieures à 3.7, transmettez _universal_newlines=True
_ au lieu de _text=True
_