2013-07-30 29 views
8

printf関数の出力をWindows上のファイルにリダイレクトしようとしています。私は関数を呼び出すためにpython3でctypesを使用しています。 TEST.TXTctypesで標準出力リダイレクト

python print 

begin 
end 
Printf function 1 
Printf function 2 
Printf function 3 

...と、以下:

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からコードを実行したとき、私は、画面上で、次を得る:私のコードです

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

begin 
end 

..andこれはTEST.TXTである:私は、例えば二dup2()文をコメントアウト

python print 

画面上のEclipseから

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") 

、:

begin 

...とTest.txtファイル内:CMDから

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

、画面上:

begin 

...とTEST.txtファイル:

python print 
end 

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

+0

は、なぜあなたは何かを印刷するには、 'ctypes'を使うのですか? – jfs

+0

related:[C共有ライブラリがpythonでstdoutに出力されないようにするにはどうすればいいですか?](http://stackoverflow.com/q/5081657/4279)( 'os.devnull'の代わりにファイルを使用してください) – jfs

+1

私は、このスレッドを読むと役立たない。私のコードは例のコードとほぼ同じです。私はctypesを必要とします。なぜなら、後でprintfsを持つCライブラリのユニットテストのために使用するからです。 –

答えて

4

CPython 3.xが使用するのと同じCランタイムを使用します(3.3ではmsvcr100.dllなど)。また、stdoutのリダイレクト前後にfflush(NULL)へのコールを含めます。プログラムがWindows APIを直接使用する場合は、WindowsのStandardOutputハンドルを適切な方法でリダイレクトしてください。

これは、DLLが独自のPOSIXファイルディスクリプタセットを持つ別のCランタイムを使用すると、複雑になる可能性があります。それは、あなたがWindows StandardOutputをリダイレクトした後に読み込まれるなら、それは正常であるはずです。

編集:

私は、Python 3.5+で実行する例を変更しました。 VC++ 14の新しい「ユニバーサルCRT」は、ctypeを介して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") 
+0

ありがとう!私は後で試してみるよ! –

+0

はい、はい、それは動作します!あなたは読書と試練の4日間後に私を救った! :) –

+0

cdll.LoadLibraryを使用して他のdllをロードし、このdll stdoutをリダイレクトしたい場合、dllがロードされる前にredirectメソッドを呼び出す必要があります。そうしないと、コンソールで実行しても機能しない可能性があります。 – lengxuehx

関連する問題