Jak zapisywać dane binarne na stdout w Pythonie 3?

104

W pythonie 2.x mogłem to zrobić:

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

Teraz jednak otrzymuję plik TypeError: can't write bytes to text stream. Czy jest jakieś tajne kodowanie, którego powinienem użyć?

Ivan Baldin
źródło
4
Znacznie lepiej byłoby znaleźć odpowiedź, która będzie działać z Pythonem 2.6+ i 3.x
sorin
2
os.writebędzie działać zarówno na Py2, jak i Py3.
David Wolever

Odpowiedzi:

170

Lepszy sposób:

import sys
sys.stdout.buffer.write(b"some binary data")
Benjamin Peterson
źródło
4
Używanie sys.stdout.bufferpozwala również robić takie rzeczy, jak używanie, shutil.copyfileobjnawet jeśli obiekt pliku źródłowego podaje bajty, a nie łańcuchy. +1
csl
1
Programy korzystające z tego nie mogą być testowane w IDLE 3:AttributeError: 'PseudoOutputFile' object has no attribute 'buffer'
Damian Yerrick
4
@DamianYerrick w IDLE (przynajmniej w Windows) pythonw.exeuruchamia IDLE, co oznacza, że ​​nie ma standardowego wyjścia. Jest emulowany przez tkinter. Fizycznie nie obsługuje bajtów. W tym przypadku .decode('UTF-8', errors='replace')twój ciąg lub uruchom, python3 -I <filename>aby uzyskać REPL zamiast używać IDLE.
Artyer
Zepsuje kolejność zapisów podczas pisania do, stderrjeśli używasz razem z print(file=sys.stderr).
Kotauskas
15
import os
os.write(1, a.tostring())

lub, os.write(sys.stdout.fileno(), …)jeśli to jest bardziej czytelne niż 1dla Ciebie.

Alex Martelli
źródło
Dzięki, zadziałało. Czuje się trochę hakersko, ale wydaje mi się, że nie jest to powszechne.
Ivan Baldin
6
Problem os.writepolega na tym, że będziesz musiał sprawdzić zwracaną wartość, ponieważ nie gwarantuje to, że wszystko zostanie zapisane.
mic_e
11

Idiomatyczny sposób na zrobienie tego, który jest dostępny tylko dla Pythona 3, to:

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

Zaletą jest to, że używa normalnego interfejsu obiektu pliku, do którego wszyscy są przyzwyczajeni w Pythonie.

Zwróć uwagę, że ustawiam closefd=Falseunikanie zamykania sys.stdoutpodczas wychodzenia z withbloku. W przeciwnym razie Twój program nie będzie mógł już drukować na standardowe wyjście. Jednak w przypadku deskryptorów plików innego rodzaju możesz pominąć tę część.

Yajo
źródło
Dlaczego się spłukujesz? Czy nie lepiej pozwolić Pythonowi zdecydować, kiedy opróżnić standardowe wyjście?
Martin
0

W przypadku, gdy chcesz określić kodowanie w python3, nadal możesz użyć polecenia bajtów, jak poniżej:

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

gdzie 1 jest odpowiednią zwykłą liczbą dla stdout -> sys.stdout.fileno ()

W przeciwnym razie, jeśli nie zależy Ci na kodowaniu, po prostu użyj:

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

Jeśli chcesz używać os.write bez kodowania, spróbuj użyć poniższego:

import os
os.write(1,b"Your string to Stdout\n")
Marco smdm
źródło
1
Programy używające os.write(sys.stdout.fileno(), some_bytes)nie będą działać w IDLE. io.UnsupportedOperation: fileno
Damian Yerrick
@DamianYerrick: Masz rację ... I tak nie należy używać IDLE do testowania czegoś takiego. Krótko: spróbuj otworzyć IDLE (miałem powłokę python3.5.1) i po prostu zaimportuj sys i sys.stdout.fileno () wyrzuci ci błąd io, ponieważ w IDLE nie jest to obsługiwana operacja :-) Zawsze jest ważne zapamiętać, w jakim środowisku pracujesz i spróbować uzyskać to, co jest możliwe;) Mam nadzieję, że to wyjaśni Twoje pytanie :-) Miłego weekendu.
Marco smdm
Wspomina się tylko o jednym sposobie zapisywania rzeczywistych danych binarnych stdout, ostatnim.
Kotauskas