9

printf 関数の出力を Windows 上のファイルにリダイレクトしようとしています。関数を呼び出すために、python3でctypesを使用しています。私のコードは次のとおりです。

import os, sys
from ctypes import *

if __name__ == '__main__':

 print("begin")
 saved_stdout=os.dup(1)
 test_file=open("TEST.TXT", "w")
 os.dup2(test_file.fileno(), 1)
 test_file.close()
 print("python print")
 cdll.msvcrt.printf(b"Printf function 1\n")
 cdll.msvcrt.printf(b"Printf function 2\n")
 cdll.msvcrt.printf(b"Printf function 3\n")
 os.dup2(saved_stdout, 1)
 print("end")

しかし、Eclipse からコードを実行すると、画面に次のように表示されます。

begin
end
Printf function 1
Printf function 2
Printf function 3

...そしてTEST.txtの以下

python print

これを cmd から実行すると、画面に次のように表示されます。

begin
end

..これは TEST.txt にあります。

python print

dup2()たとえば、2番目のステートメントをコメントアウトすると

import os, sys
from ctypes import *
if __name__ == '__main__':

    print("begin")
    saved_stdout=os.dup(1)
    test_file=open("TEST.TXT", "w")
    os.dup2(test_file.fileno(), 1)
    test_file.close()
    print("python print")
    cdll.msvcrt.printf(b"Printf function 1\n")
    cdll.msvcrt.printf(b"Printf function 2\n")
    cdll.msvcrt.printf(b"Printf function 3\n")
    #os.dup2(saved_stdout, 1)
    print("end")

Eclipse の画面で:

begin

...そして TEST.txt ファイル内:

python print
end
Printf function 1
Printf function 2
Printf function 3

画面上のcmdから:

begin

...そして TEST.txt ファイル内:

python print
end

私は今完全に混乱しています。ここ StackOverflow のすべてのリダイレクト スレッドを読みましたが、何が起こっているのか理解できません。とにかく、私が集めたのは、C関数がファイル記述子に直接バインドされているstdoutにアクセスするのに対し、pythonはそのために特別なオブジェクト-stdoutファイルオブジェクトを使用するということです。そのため、エレメンタリーsys.stdout=*something*は ctypes では機能しません。os.fdopen(1)dup2-ed 出力を試してから、flush()すべてのprintfステートメントの後に呼び出すことさえしましたが、これは再び機能しません。私は今、まったくアイデアがありません。誰かがこれに対する解決策を持っていれば幸いです。

4

1 に答える 1

7

CPython 3.x が使用するものと同じ C ランタイムを使用します (例: 3.3 の場合は msvcr100.dll)。fflush(NULL)リダイレクトの前後にへの呼び出しも含めますstdoutStandardOutputプログラムが Windows API を直接使用する場合は、Windows ハンドルをリダイレクトしてください。

DLL が独自の POSIX ファイル記述子セットを持つ別の C ランタイムを使用する場合、これは複雑になる可能性があります。とはいえ、 Windows をリダイレクトした後にロードされれば問題ありませんStandardOutput

編集:

Python 3.5 以降で実行するように例を修正しました。VC++ 14 の新しい "Universal CRT" により、ctypes を介して C 標準 I/O を使用することがはるかに難しくなります。

import os
import sys
import ctypes, ctypes.util

kernel32 = ctypes.WinDLL('kernel32')

STD_OUTPUT_HANDLE = -11

if sys.version_info < (3, 5):
    libc = ctypes.CDLL(ctypes.util.find_library('c'))
else:
    if hasattr(sys, 'gettotalrefcount'): # debug build
        libc = ctypes.CDLL('ucrtbased')
    else:
        libc = ctypes.CDLL('api-ms-win-crt-stdio-l1-1-0')

    # VC 14.0 doesn't implement printf dynamically, just
    # __stdio_common_vfprintf. This take a va_array arglist,
    # which I won't implement, so I escape format specificiers.

    class _FILE(ctypes.Structure):
        """opaque C FILE type"""

    libc.__acrt_iob_func.restype = ctypes.POINTER(_FILE)    

    def _vprintf(format, arglist_ignored):
        options = ctypes.c_longlong(0) # no legacy behavior
        stdout = libc.__acrt_iob_func(1)
        format = format.replace(b'%%', b'\0')
        format = format.replace(b'%', b'%%')
        format = format.replace(b'\0', b'%%')
        arglist = locale = None        
        return libc.__stdio_common_vfprintf(
            options, stdout, format, locale, arglist)

    def _printf(format, *args):
        return _vprintf(format, args)

    libc.vprintf = _vprintf
    libc.printf = _printf
def do_print(label):
    print("%s: python print" % label)
    s = ("%s: libc _write\n" % label).encode('ascii')
    libc._write(1, s, len(s))
    s = ("%s: libc printf\n" % label).encode('ascii')
    libc.printf(s)
    libc.fflush(None) # flush all C streams

if __name__ == '__main__':
    # save POSIX stdout and Windows StandardOutput
    fd_stdout = os.dup(1)
    hStandardOutput = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)

    do_print("begin")

    # redirect POSIX and Windows
    with open("TEST.TXT", "w") as test:
        os.dup2(test.fileno(), 1)
        kernel32.SetStdHandle(STD_OUTPUT_HANDLE, libc._get_osfhandle(1))

    do_print("redirected")

    # restore POSIX and Windows
    os.dup2(fd_stdout, 1)
    kernel32.SetStdHandle(STD_OUTPUT_HANDLE, hStandardOutput)

    do_print("end")
于 2013-07-30T17:46:13.250 に答える