Comment écrire des données binaires sur stdout en python 3?

103

En python 2.x, je pourrais faire ceci:

import sys, array
a = array.array('B', range(100))
a.tofile(sys.stdout)

Maintenant, cependant, j'obtiens un TypeError: can't write bytes to text stream. Y a-t-il un encodage secret que je devrais utiliser?

Ivan Baldin
la source
4
Il serait beaucoup mieux de trouver une réponse qui fonctionnera avec Python 2.6+ et 3.x
sorin
2
os.writefonctionnera à la fois sur Py2 et Py3.
David Wolever

Réponses:

169

Une meilleure façon:

import sys
sys.stdout.buffer.write(b"some binary data")
Benjamin Peterson
la source
4
L'utilisation sys.stdout.buffervous permet également d'effectuer des opérations telles que l'utilisation shutil.copyfileobjmême lorsque l'objet du fichier source donne des octets et non des chaînes. +1
csl
1
Les programmes utilisant ceci ne peuvent pas être testés dans IDLE 3:AttributeError: 'PseudoOutputFile' object has no attribute 'buffer'
Damian Yerrick
4
@DamianYerrick en IDLE (au moins sous Windows) pythonw.exeexécute IDLE, ce qui signifie qu'il n'y a pas de sortie standard. Il est émulé avec tkinter. Il ne peut physiquement pas gérer les octets. Dans ce cas, .decode('UTF-8', errors='replace')votre chaîne, ou exécutez python3 -I <filename>pour obtenir un REPL au lieu d'utiliser IDLE.
Artyer
Désordre l'ordre des écritures lors de l'écriture stderrsi vous utilisez avec print(file=sys.stderr).
Kotauskas
15
import os
os.write(1, a.tostring())

ou, os.write(sys.stdout.fileno(), …)si c'est plus lisible que 1pour vous.

Alex Martelli
la source
Merci, cela a fonctionné. On se sent un peu hack-ish mais je suppose que ce n'est pas une chose courante à faire.
Ivan Baldin
6
Le problème avec os.writeest que vous devrez vérifier la valeur de retour, car cela ne garantit pas que tout sera écrit.
mic_e
10

Une façon idiomatique de le faire, qui n'est disponible que pour Python 3, est:

with os.fdopen(sys.stdout.fileno(), "wb", closefd=False) as stdout:
    stdout.write(b"my bytes object")
    stdout.flush()

L'avantage est qu'il utilise l'interface d'objet fichier normale, à laquelle tout le monde est habitué en Python.

Notez que je suis en train closefd=Falsede régler pour éviter de fermer sys.stdoutlorsque je quitte le withbloc. Sinon, votre programme ne pourrait plus imprimer sur stdout. Cependant, pour d'autres types de descripteurs de fichiers, vous pouvez ignorer cette partie.

Yajo
la source
Pourquoi rincez-vous? N'est-il pas préférable de laisser Python décider quand vider stdout?
Martin
0

Si vous souhaitez spécifier un encodage en python3, vous pouvez toujours utiliser la commande bytes comme ci-dessous:

import os
os.write(1,bytes('Your string to Stdout','UTF-8'))

où 1 est le nombre habituel correspondant pour stdout -> sys.stdout.fileno ()

Sinon, si vous ne vous souciez pas de l'encodage, utilisez simplement:

import sys
sys.stdout.write("Your string to Stdout\n")

Si vous souhaitez utiliser os.write sans encodage, essayez d'utiliser ce qui suit:

import os
os.write(1,b"Your string to Stdout\n")
Marco smdm
la source
1
Les programmes utilisant os.write(sys.stdout.fileno(), some_bytes)ne fonctionneront pas en IDLE. io.UnsupportedOperation: fileno
Damian Yerrick
@DamianYerrick: Vous avez raison ... l'IDLE ne doit de toute façon pas être utilisé pour tester quelque chose comme ça. Bref: essayez d'ouvrir l'IDLE (j'avais le shell python3.5.1) et importez simplement sys et sys.stdout.fileno () cela vous enverra une erreur io, car en IDLE, cette opération n'est pas prise en charge :-) C'est toujours important pour vous rappeler dans quel environnement vous travaillez et essayer d'obtenir ce qui est possible;) J'espère que cela clarifiera votre requête :-) Passez un bon week-end.
Marco smdm
Vous ne mentionnez qu'une seule façon d'écrire des données binaires réelles stdout, la dernière.
Kotauskas