Question

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 communique () 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'

Toutefois, j'aimerais travailler avec la sortie sous forme de chaîne Python normale. 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 ce que l ' binascii.b2a_qp () La méthode est pour, mais quand je l'ai essayée, j'ai à nouveau 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'

Quelqu'un sait-il comment reconvertir la valeur d'octets en chaîne? Je veux dire, en utilisant les "piles" au lieu de le faire manuellement. Et j'aimerais que tout se passe bien avec Python 3.

Était-ce utile?

La solution

Vous devez décoder l'objet octet 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'

Autres conseils

Je pense que cette façon est facile:

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

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'
b'hello'.decode(encoding)

Sur Python 3

encoding = 'utf-8'
str(b'hello', encoding)

Si vous ne connaissez pas l'encodage, pour lire les entrées binaires dans une chaîne compatible avec Python 3 et Python 2, utilisez l'ancien MS-DOS codage cp437 :

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

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

L'encodage étant inconnu, attendez-vous à ce que les symboles non anglais traduisent en caractères 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

Il en va de même pour latin-1 , qui était populaire (par défaut?) pour Python 2. Voir les points manquants dans Disposition de page de code - c’est là que Python s’arrête avec un ordinal non compris dans la plage .

UPDATE 20150604 : selon certaines rumeurs, Python 3 aurait une stratégie d'erreur surrogateescape pour le codage de données dans des données binaires sans perte de données ni crash, mais des tests de conversion doivent être effectués . [binaire] - > [str] - > [binary] pour valider performance et 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 des résultats incohérents 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.

MISE À JOUR 20170119 : j'ai décidé d'implémenter le décodage d'échappement slash qui fonctionne à la fois pour Python 2 et Python 3. Il devrait être plus lent que la solution cp437 , mais cela devrait produire < strong> résultats identiques sur chaque version de 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'))

Dans Python 3 , le codage par défaut est "utf-8" , vous pouvez donc utiliser directement:

b'hello'.decode()

qui est équivalent à

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

D'autre part, en Python 2 , codage par défaut, l'encodage de chaîne par défaut. Ainsi, vous devriez utiliser:

b'hello'.decode(encoding)

codage est le codage souhaité.

Remarque: pour les 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 devez savoir quel encodage utiliser. Et je crois que Windows utilise 'windows-1252'. Cela importera seulement 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 de données binaires et textuelles: il ne peut pas convertir de façon magique entre eux, car il ne connaît pas l’encodage sauf si vous le lui indiquez. ! 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'est-à-dire.

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

Alors que la réponse de @Aaron Maenpaa fonctionne, l'utilisateur a récemment posé des questions :

  

Y at-il plus simple? 'fhand.read (). decode ("ASCII")' [...] Il est si long!

Vous pouvez utiliser:

command_stdout.decode()

decode () contient argument standard :

  

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

Pour interpréter une séquence d'octets en tant que texte, vous devez connaître la 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. Noms de fichiers sous Unix peut être n’importe quelle séquence d’octets sauf barre oblique b '/' et 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 un mojibake . si vous utilisez un encodage incompatible incorrect:

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

Les données sont corrompues mais votre programme n’a toujours pas conscience d’un échec. a eu lieu.

En général, le codage de caractères à utiliser ne figure pas dans 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 il existe donc un module chardet 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 chaîne Python à l'aide de os.fsdecode () fonction qui réussit même pour indécodable noms de fichiers (il utilise Gestionnaire de code sys.getfilesystemencoding () et surrogateescape 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 , sous-processus utilise locale.getpreferredencoding (False) pour décoder des octets, par exemple, il peut s'agir cp1252 sous Windows.

Pour décoder le flux d'octets à la volée, io.TextIOWrapper () pourrait être utilisé: exemple .

Différentes commandes peuvent utiliser différents codages de caractères pour leur En sortie, par exemple, la commande interne dir ( cmd ) peut utiliser cp437. Décoder ses en sortie, vous pouvez passer explicitement le codage (Python 3.6 +):

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

Les noms de fichier peuvent différer de os.listdir () (qui utilise Windows Unicode API), par exemple, '\ xb6' peut être remplacé par '\ x14' & # 8212; Python Le codec cp437 mappe b '\ x14' pour contrôler le caractère U + 0014 au lieu de U + 00B6 (& # 182;). Pour prendre en charge les noms de fichiers comportant des caractères Unicode arbitraires, voir Décoder la sortie poweshell contenant éventuellement des caractères unicode non asci dans une chaîne python

Comme cette question concerne en fait la sortie de sous-processus , vous avez une approche plus directe disponible car Popen accepte un encodage 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 consiste à 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 décoder appel:

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

Si vous devriez obtenir ce qui suit en essayant decode () :

  

AttributeError: l'objet 'str' n'a pas d'attribut 'décoder'

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 des fins de ligne \ r \ n ), 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 vos fins de ligne seront doublées (en \ r \ r \ n ), ce qui conduit à 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 un octet en chaîne :

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
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 souhaitez convertir des octets, pas uniquement 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.

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 stdout, utilisez sys.stdout.buffer.write (b'abc ') .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top