Question Convertir des octets en une 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 communicate () 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 travailler avec la sortie comme une 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'est ce que binascii.b2a_qp () méthode est pour, mais quand je l'ai essayé, j'ai encore 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 convertir la valeur des octets en chaîne? Je veux dire, en utilisant les "batteries" au lieu de le faire manuellement. Et j'aimerais que ça marche avec Python 3.


1236
2018-03-03 12:23


origine


Réponses:


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'

2036
2018-03-03 12:26



Je pense que c'est facile:

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

119
2017-08-22 12:57



Vous devez décoder la chaîne d'octets et la transformer en une chaîne de caractères (unicode).

b'hello'.decode(encoding)

ou

str(b'hello', encoding)

99
2018-03-03 12:28



Si vous ne connaissez pas l'encodage, alors pour lire l'entrée binaire dans une chaîne de manière compatible avec Python 3 et Python 2, 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'))

Étant donné que l'encodage est inconnu, attendez-vous à ce que les symboles non anglais soient traduits en caractères de cp437 (Les caractères anglais ne sont pas traduits, car ils correspondent à la plupart des codages à un seul octet et UTF-8).

Le décodage d'une entrée binaire arbitraire en UTF-8 n'est pas sûr, car vous pouvez 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

de même pour latin-1, qui était populaire (par défaut?) pour Python 2. Voir les points manquants dans Disposition de la page de codes - C'est là que Python s'étouffe avec tristement ordinal not in range.

MISE À JOUR 20150604: Il y a des rumeurs que Python 3 a surrogateescape stratégie d'erreur pour l'encodage de données dans des données binaires sans perte de données et de plantages, mais il faut des tests de conversion [binary] -> [str] -> [binary] valider à la fois la performance et la fiabilité.

MISE À JOUR 20170116: Merci au commentaire de Nearoo - il y a aussi une possibilité de couper échapper tous les octets inconnus avec backslashreplace gestionnaire d'erreurs. Cela ne fonctionne que pour Python 3, donc même avec cette solution de contournement, vous aurez 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 slash échapper décodage qui fonctionne à la fois pour Python 2 et Python 3. Il devrait être plus lent que cp437 solution, mais il devrait produire 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'))

57
2017-12-17 14:23



Je pense que ce que vous voulez réellement 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 n'a d'importance que si vous avez des caractères inhabituels (non-ascii) dans votre contenu, mais cela fera une différence.

Au fait, le fait que cela ait de l'importance est la raison pour laquelle Python a utilisé deux types différents de données binaires et de texte: il ne peut pas se convertir magiquement entre eux parce qu'il ne connaît pas l'encodage sauf si vous le lui dites! La seule façon que vous sachiez est de lire la documentation de Windows (ou lisez-la ici).


32
2017-07-18 19:51



En Python 3, l'encodage par défaut est "utf-8", donc vous pouvez utiliser directement:

b'hello'.decode()

ce qui est équivalent à

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

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

b'hello'.decode(encoding)

encoding est l'encodage que vous voulez.

Remarque: support pour les arguments de mots-clés a été ajouté dans Python 2.7.


30
2018-06-29 14:21



Définissez universal_newlines sur True, c'est-à-dire

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

26
2018-01-21 15:31



Tandis que La réponse de @Aaron Maenpaa fonctionne, un utilisateur récemment demandé

Y a-t-il un moyen plus simple? 'fhand.read (). decode ("ASCII")' [...] C'est tellement long!

Vous pouvez utiliser

command_stdout.decode()

decode() a un argument standard

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


15
2017-11-13 10:24



Pour interpréter une séquence d'octets comme un texte, vous devez connaître codage de caractères correspondant:

unicode_text = bytestring.decode(character_encoding)

Exemple:

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

ls La commande peut produire une sortie qui ne peut pas être interprétée comme du texte. Noms de fichiers sur Unix peut être une séquence d'octets sauf slash b'/' et zéro b'\0':

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

Essayer de décoder une telle soupe d'octets en utilisant l'encodage utf-8 UnicodeDecodeError.

Cela peut être pire. Le décodage peut échouer silencieusement et produire mojibake si vous utilisez un mauvais encodage incompatible:

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

Les données sont corrompues mais votre programme ne sait pas qu'un échec s'est produit.

En général, le codage de caractères à utiliser n'est pas incorporé 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 par conséquent chardet module existe qui peut deviner le codage de caractères. Un seul script Python peut utiliser plusieurs codages de caractères à différents endroits.


ls la sortie peut être convertie en une chaîne Python en utilisant os.fsdecode() fonction qui réussit même pour indécodable noms de fichiers (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 tu passes universal_newlines=True paramètre alors subprocess les usages locale.getpreferredencoding(False) pour décoder des octets, par exemple, il peut être cp1252 sur Windows.

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

Différentes commandes peuvent utiliser des codages de caractères différents pour leur sortie, par exemple, dir commande interne (cmd) peut utiliser cp437. Pour décoder son sortie, vous pouvez passer l'encodage explicitement (Python 3.6+):

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

Les noms de fichiers peuvent différer de os.listdir() (qui utilise Windows API Unicode), par exemple, '\xb6' peut être remplacé par '\x14'-Python's cartes de codec cp437 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 Décoder la sortie de l'en-tête contenant éventuellement des caractères unicode non-ascii dans une chaîne python


10
2017-11-16 09:43



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

AttributeError: 'str' object has no attribute 'decode'

Vous pouvez également spécifier le type d'encodage directement dans une distribution:

>>> my_byte_str
b'Hello World'

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

5
2017-11-22 04:20