在Python中重复重定向低级别stdin

时间:2013-11-15 11:43:06

标签: c++ python io-redirection

这是我的情况:有一些旧的C ++代码,我试图为它创建一个简单的Python包装器。此代码最初设计为从命令行驱动,并期望输入文件通过管道输入。为了最大限度地干扰这段代码,我将main函数转换为vanilla extern“C”函数并将其编译成一个共享库,我在Python中加载并通过ctypes驱动。请注意,我不想通过系统调用来驱动此代码,因为它最终会在集群上运行并执行数十万次。

从这里开始,我需要控制较低级别的stdin,以便设置输入文件,以便可以通过C ++代码中的各种“cin”语句读取它。

从我到目前为止所学到的,似乎理论上我可以通过使用os库覆盖stdin文件描述符来做到这一点。我编写了以下示例,它似乎在Python中运行得非常好(基于此线程中的各种信息:Python version of freopen()):

import os
import sys

def freopen(f,option,stream):
    oldf = open(f,option)
    oldfd = oldf.fileno()
    newfd = stream.fileno()
    os.close(newfd)
    os.dup2(oldfd, newfd)

# Original stdout file descriptor:
fd = sys.stdout.fileno()
orig_stream = os.fdopen(os.dup(fd), 'w') 

# Test writing to file:
freopen("hello","w",sys.stdout)
print "world"
sys.stdout.flush()
freopen("hello2","w",sys.stdout)
print "world2"
sys.stdout.flush()

# Restore stdout to normal
os.dup2(orig_stream.fileno(),sys.stdout.fileno())

print "back to normal!"

# Test reading:
freopen("hello","r",sys.stdin)
print sys.stdin.readlines()

freopen("hello2","r",sys.stdin)
print sys.stdin.readlines()

这会产生输出

back to normal!
['world\n']
['world2\n']

(以及两个文件“hello”和“hello2”)对我来说。很好!不幸的是,当我的C ++库函数读取stdin流时,它似乎不起作用。奇怪的是,它第一次工作很好,但第二次失败;没有错误发生,似乎更像是stdin流对于第二次重定向而言是空的。当我重定向stdout而不是stdin时,似乎工作正常。

我需要做些什么特别的事情来重置第二次传递的低级别stdin流吗?

额外信息:在c ++方面,stdin正好被读取:

while (getline(cin,line)) {
        //    do stuff with line...

如果解决方案是“仅限Linux”,我也不会太担心。

编辑:我正在运行Python 2.7.1

更新:嗯,也许库代码做了一些奇怪的事情,因为如果我将测试代码的“阅读测试”部分更改为:

# Test reading:
freopen("hello","r",sys.stdin)
os.system("cat")

freopen("hello2","r",sys.stdin)
os.system("cat")

然后是预期的输出

back to normal!
world
world2

仍在制作中。因此,'cat'处理stdin似乎与stdin重定向的这种方法完全兼容。嗯。当然,通过这种方法创建了一个新的“cat”进程,并且每次都将新的stdin连接到它,因此它与调用我的库函数并不完全相同。我将尝试创建一个最小的库函数来重现问题...

1 个答案:

答案 0 :(得分:0)

啊哈!事实证明,这个问题非常有用:cin.ignore() and cin.clear() in C++

所以看起来至少在C ++中,当一个流到达eof时,它会将各种错误标志设置为true(http://www.cplusplus.com/reference/ios/ios/good/)。这可以防止在流上执行进一步的操作。当我的库代码是一个独立的可执行文件时,这种行为很好,因为该程序只会尝试从cin读取一次文件。但是,随着代码转换为库,当我​​的循环返回到第二次调用库函数时,库函数仍在处理相同的stdin流。流仍然设置了错误标志,因此第二次传递中的cin读取失败。

为了解决这个问题,我添加了

std::cin.clear();
在尝试cin读取之前,

到库代码。这会重置流的错误标志,然后可以正常读取。

我不确定是否可以在Python端处理流标志的重置;这很有用。