如何在Windows中重定向Python中的C级流?

前端之家收集整理的这篇文章主要介绍了如何在Windows中重定向Python中的C级流?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
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...')

猜你在找的Windows相关文章