web-dev-qa-db-fra.com

Convertir des octets en chaîne?

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.

1818
Tomas Sedovic

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'
2962
Aaron Maenpaa

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)
163
dF.

Je pense que cette façon est facile:

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

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'))
76
anatoly techtonik

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)

encoding est l'encodage souhaité.

Remarque: le support des arguments de mots clés a été ajouté dans Python 2.7.

67
lmiguelvargasf

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).

38
mcherm

Définissez universal_newlines sur True, c.-à-d.

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

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

19
serv-inc

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é'
15
wim

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

13
jfs

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'
8
Broper

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.

5
bers

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
4
eafloresf

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
2
Inconnu

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').

2
Zhichang Yu
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
Leonardo Filipe

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.

1
HCLivess

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_

0
Boris