2009-08-02 5 views
94

Używam Pythona ftplib do napisania małego klienta FTP, ale niektóre funkcje w pakiecie nie zwracają ciągów, ale drukuj do stdout. Chcę przekierować stdout do obiektu, z którego będę mógł odczytać wynik.Czy mogę przekierować standardowe wyjście w pythonie na jakiś bufor łańcuchowy?

wiem stdout może być przekierowana do dowolnej zwykłego pliku z:

stdout = open("file", "a") 

Ale wolę metodę, która nie korzysta z dysku lokalnego.

Szukam czegoś takiego, jak BufferedReader w języku Java, które może być używane do zawijania bufora do strumienia.

+0

nie sądzę 'stdout = o pióro ("plik", "a") 'samo przekieruje wszystko. – Alexey

Odpowiedz

148
from cStringIO import StringIO 
import sys 

old_stdout = sys.stdout 
sys.stdout = mystdout = StringIO() 

# blah blah lots of code ... 

sys.stdout = old_stdout 

# examine mystdout.getvalue() 
+40

+1, nie musisz zachowywać odniesienia do oryginalnego obiektu 'stdout', ponieważ jest on zawsze dostępny w' sys .__ stdout__'. Zobacz http://docs.python.org/library/sys.html#sys.__stdout__. –

+71

Cóż, to interesująca debata. Absolutny oryginalny stdout jest dostępny, ale przy takiej wymianie lepiej jest użyć jawnego zapisu, tak jak zrobiłem, ponieważ ktoś inny mógłby zastąpić standardowe wyjście i jeśli użyjesz __stdout__, odrzuciłbyś ich zamiennik. –

+5

czy ta operacja w jednym wątku zmieniłaby zachowanie innych wątków? Mam na myśli czy jest bezpieczny dla wątków? –

34

Wystarczy dodać do odpowiedzi Neda powyżej: można to wykorzystać, aby przekierować dane wyjściowe do dowolny obiekt, który implementuje metodę write (STR).

Można to wykorzystać do dobrego efektu do "catch" wyjścia wyjściowego w aplikacji GUI.

Oto głupi przykład w PyQt:

import sys 
from PyQt4 import QtGui 

class OutputWindow(QtGui.QPlainTextEdit): 
    def write(self, txt): 
     self.appendPlainText(str(txt)) 

app = QtGui.QApplication(sys.argv) 
out = OutputWindow() 
sys.stdout=out 
out.show() 
print "hello world !" 
+0

To nie działa, blokuje aplikację, ale nie mogę powiedzieć dlaczego. –

+3

Działa dla mnie z pythonem 2.6 i PyQT4. Wydaje się dziwne, że działający kod głosowania nie działa, gdy nie możesz powiedzieć, dlaczego to nie działa! –

+5

nie zapomnij również dodać funkcji flush()! – Will

4

Począwszy od Pythona 2.6 można używać niczego realizacji TextIOBase API z modułu IO zamiennik. To rozwiązanie umożliwia także użycie sys.stdout.buffer.write() w Pythonie 3 do napisania (już) zakodowanych ciągów bajtów do stdout (patrz stdout in Python 3). Używanie StringIO nie zadziałałoby wtedy, ponieważ ani sys.stdout.encoding ani ani ani ani byłyby dostępne.

Roztwór stosując TextIOWrapper:

import sys 
from io import TextIOWrapper, BytesIO 

# setup the environment 
old_stdout = sys.stdout 
sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding) 

# do something that writes to stdout or stdout.buffer 

# get output 
sys.stdout.seek(0)  # jump to the start 
out = sys.stdout.read() # read output 

# restore stdout 
sys.stdout.close() 
sys.stdout = old_stdout 

To rozwiązanie działa w Pythonie 2> = 2,6 i Python 3.

Należy pamiętać, że nasze nowe sys.stdout.write() akceptuje tylko ciągi znaków Unicode i sys.stdout.buffer.write() akceptuje tylko ciągi bajtów. To może nie być prawda w przypadku starego kodu, ale często ma miejsce w przypadku kodu, który jest zbudowany do uruchamiania w Pythonie 2 i 3 bez wprowadzania zmian, co ponownie często wykorzystuje numer sys.stdout.buffer.

Można zbudować niewielką zmianę, która akceptuje Unicode i ciągi bajtów dla write():

class StdoutBuffer(TextIOWrapper): 
    def write(self, string): 
     try: 
      return super(StdoutBuffer, self).write(string) 
     except TypeError: 
      # redirect encoded byte strings directly to buffer 
      return super(StdoutBuffer, self).buffer.write(string) 

Nie trzeba ustawić kodowanie buforowania sys.stdout.encoding, ale to pomaga przy korzystaniu ta metoda testowania/porównywania danych wyjściowych skryptu.

+0

Ta odpowiedź pomogła mi podczas konfigurowania standardowego parametru obiektu Environment do użycia z core.py. Httpie. – fragorl

2

Ta metoda przywraca sys.stdout, nawet jeśli istnieje wyjątek. Otrzymuje również dane wyjściowe przed wyjątkiem.

real_stdout = sys.stdout 
fake_stdout = io.BytesIO() 
try: 
    sys.stdout = fake_stdout 
    # do what you gotta do to create some output 
finally: 
    sys.stdout = real_stdout 
    output_string = fake_stdout.getvalue() 
    fake_stdout.close() 
    # do what you want with the output_string 

I tylko przetestowane w Pythonie 2.7.10, ale io.BytesIO jest podobno być replacement for StringIO in Python 3.

2

W Python3.6 moduły StringIO i cStringIO zostaną usunięte, należy użyć io.StringIO instead.So należy zrobić to jak pierwszy odpowiedź:

import sys 
from io import StringIO 

old_stdout = sys.stdout 
old_stderr = sys.stderr 
my_stdout = sys.stdout = StringIO() 
my_stderr = sys.stderr = StringIO() 

# blah blah lots of code ... 

sys.stdout = self.old_stdout 
sys.stderr = self.old_stderr 

// if you want to see the value of redirect output, be sure the std output is turn back 
print(my_stdout.getvalue()) 
print(my_stderr.getvalue()) 

my_stdout.close() 
my_stderr.close() 
+0

Możesz poprawić jakość swojej odpowiedzi, wyjaśniając, jak działa powyższy kod i jak to poprawić w porównaniu z sytuacją pytającego. – toonice

+0

Dziękuję za poradę. – haofly

0

kierownik kontekst python3:

import sys 
from io import StringIO 


class redirected_stdout: 
    def __init__(self): 
     self._stdout = None 
     self._string_io = None 

    def __enter__(self): 
     self._stdout = sys.stdout 
     sys.stdout = self._string_io = StringIO() 
     return self 

    def __exit__(self, type, value, traceback): 
     sys.stdout = self._stdout 

    @property 
    def string(self): 
     return self._string_io.getvalue() 

stosowanie tak:

>>> with redirected_stdout() as out: 
>>>  print('asdf') 
>>>  s = out.string 
>>>  print('bsdf') 
>>> print(s, out.string) 
'asdf\n' 'asdf\nbsdf\n'