Eli Bendersky已经详细解释了如何“
Redirecting all kinds of stdout in Python”,特别是重定向C级流,例如stdout共享库(dll).但是,该示例在Linux中并不适用于Windows,主要是由于以下几行:
libc = ctypes.CDLL(None) c_stdout = ctypes.c_void_p.in_dll(libc = ctypes.CDLL(None),'stdout')
我们怎样才能让它在Windows中运行?
解决方法
我在
Drekin’s code找到了答案.基于此,我对
Eli Bendersky’s example做了一个小改动:
更新:此代码已经在Windows上的64位64位和Linux上的64位64位上进行了测试.对于Windows上的Python 3.5,请参阅eryksun的评论.
from contextlib import contextmanager import ctypes import io import os import sys import tempfile import ctypes.util from ctypes import * import platform if platform.system() == "Linux": libc = ctypes.CDLL(None) c_stdout = ctypes.c_void_p.in_dll(libc,'stdout') if platform.system() == "Windows": class FILE(ctypes.Structure): _fields_ = [ ("_ptr",c_char_p),("_cnt",c_int),("_base",("_flag",("_file",("_charbuf",("_bufsize",("_tmpfname",] # Gives you the name of the library that you should really use (and then load through ctypes.CDLL msvcrt = CDLL(ctypes.util.find_msvcrt()) libc = msvcrt # libc was used in the original example in _redirect_stdout() iob_func = msvcrt.__iob_func iob_func.restype = POINTER(FILE) iob_func.argtypes = [] array = iob_func() s_stdin = addressof(array[0]) c_stdout = addressof(array[1]) @contextmanager def stdout_redirector(stream): # The original fd stdout points to. Usually 1 on POSIX systems. original_stdout_fd = sys.stdout.fileno() def _redirect_stdout(to_fd): """Redirect stdout to the given file descriptor.""" # Flush the C-level buffer stdout libc.fflush(c_stdout) # Flush and close sys.stdout - also closes the file descriptor (fd) sys.stdout.close() # Make original_stdout_fd point to the same file as to_fd os.dup2(to_fd,original_stdout_fd) # Create a new sys.stdout that points to the redirected fd sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd,'wb')) # Save a copy of the original stdout fd in saved_stdout_fd saved_stdout_fd = os.dup(original_stdout_fd) try: # Create a temporary file and redirect stdout to it tfile = tempfile.TemporaryFile(mode='w+b') _redirect_stdout(tfile.fileno()) # Yield to caller,then redirect stdout back to the saved fd yield _redirect_stdout(saved_stdout_fd) # Copy contents of temporary file to the given stream tfile.flush() tfile.seek(0,io.SEEK_SET) stream.write(tfile.read()) finally: tfile.close() os.close(saved_stdout_fd) if __name__ == '__main__': f = io.BytesIO() print('...') with stdout_redirector(f): print('foobar') print(12) libc.puts(b'this comes from C') os.system('echo and this is from echo') print('Got stdout:"\n{0}\n"'.format(f.getvalue().decode('utf-8'))) print('Resuming normal operation...')