2011-10-25 87 views
2

我有一个简单的C++程序,我试图通过python脚本执行。 (我对编写脚本非常陌生),而且我无法通过管道读取输出。从我所看到的,看起来像readline()在没有EOF的情况下无法工作,但我希望能够在程序中间读取并让脚本响应输出的内容。而不是读输出,它只是挂起 的Python脚本:子进程readline挂起等待EOF

#!/usr/bin/env python 
import subprocess 
def callRandomNumber(): 
    print "Running the random guesser" 
    rng=subprocess.Popen("./randomNumber", stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) 
    i=50 
    rng.stdin.write("%d\n" % i) 
    output=rng.stdout.readline() 
    output=rng.stdout.readline() 
callRandomNumber() 

和C++文件,其产生一个与100之间的随机数,然后检查用户猜测直至他们猜测正确

#include<iostream> 
#include<cstdlib> 
using namespace std; 

int main(){ 
    cout<<"This program generates a random number from 1 to 100 and asks the user to enter guesses until they succuessfully guess the number. It then tells the user how many guesses it took them"<<endl; 
    srand(time(NULL)); 
    int num=rand()%100; 
    int guessCount=0; 
    int guess=-1; 
    cout<<"Please enter a number: "; 
    cin>>guess; 
    while(guess!=num){ 
    if(guess>num){cout<<"That guess is too high. Please guess again: ";} 
    else{cout<<"That guess is too low. Please guess again: ";} 
    cin>>guess; 
    guessCount++; 
    } 
    cout<<"Congratulations! You solved it in "<<guessCount<<" guesses!"<<endl; 
    return 0; 
} 

最终的目标是让脚本用二进制搜索来解决问题,但现在我只是希望能够在不读取文件末尾的情况下读取一行文件

回答

3

由于@Ron Reiter pointed out,你不能使用readline()因为cout不会隐打印换行符 - 你要么需要std::endl"\n"这里。

对于交互使用,当你不能改变孩子的程序,pexpect module提供了多种方便的方法(和一般的it solves for free: input/output directly from/to terminal (outside of stdin/stdout) and block-buffering issues):

#!/usr/bin/env python 
import sys 

if sys.version_info[:1] < (3,): 
    from pexpect import spawn, EOF # $ pip install pexpect 
else: 
    from pexpect import spawnu as spawn, EOF # Python 3 

child = spawn("./randomNumber") # run command 
child.delaybeforesend = 0 
child.logfile_read = sys.stdout # print child output to stdout for debugging 
child.expect("enter a number: ") # read the first prompt 
lo, hi = 0, 100 
while lo <= hi: 
    mid = (lo + hi) // 2 
    child.sendline(str(mid)) # send number 
    index = child.expect([": ", EOF]) # read prompt 
    if index == 0: # got prompt 
     prompt = child.before 
     if "too high" in prompt: 
      hi = mid - 1 # guess > num 
     elif "too low" in prompt: 
      lo = mid + 1 # guess < num 
    elif index == 1: # EOF 
     assert "Congratulations" in child.before 
     child.close() 
     break 
else: 
    print('not found') 
    child.terminate() 
sys.exit(-child.signalstatus if child.signalstatus else child.exitstatus) 

它的工作原理,但它是一个二进制搜索因此(traditionally) there could be bugs

下面是一个使用subprocess模块比较类似的代码:

#!/usr/bin/env python 
from __future__ import print_function 
import sys 
from subprocess import Popen, PIPE 

p = Popen("./randomNumber", stdin=PIPE, stdout=PIPE, 
      bufsize=1, # line-buffering 
      universal_newlines=True) # enable text mode 
p.stdout.readline() # discard welcome message: "This program gener... 

readchar = lambda: p.stdout.read(1) 
def read_until(char): 
    buf = [] 
    for c in iter(readchar, char): 
     if not c: # EOF 
      break 
     buf.append(c) 
    else: # no EOF 
     buf.append(char) 
    return ''.join(buf).strip() 

prompt = read_until(':') # read 1st prompt 
lo, hi = 0, 100 
while lo <= hi: 
    mid = (lo + hi) // 2 
    print(prompt, mid) 
    print(mid, file=p.stdin) # send number 
    prompt = read_until(':') # read prompt 
    if "Congratulations" in prompt: 
     print(prompt) 
     print(mid) 
     break # found 
    elif "too high" in prompt: 
     hi = mid - 1 # guess > num 
    elif "too low" in prompt: 
     lo = mid + 1 # guess < num 
else: 
    print('not found') 
    p.kill() 
for pipe in [p.stdin, p.stdout]: 
    try: 
     pipe.close() 
    except OSError: 
     pass 
sys.exit(p.wait()) 
1

我很确定在你的C++程序将会导致readlines将返回。

+0

是的,但我希望能够使脚本现有的方案,而不是修改方案,以适应脚本 –

+2

没有通用的方法以“解决”EOF问题。否则,你怎么知道这条线实际上已经结束?如果你知道该程序,不要阅读直到换行,只需阅读你需要的字符的确切数量。否则,使其返回一个换行符。你的另一种选择是使用超时,这非常糟糕。 –

+0

你不需要阅读确切的金额。 [只要几乎同步就足够了](http://stackoverflow.com/a/23795689/4279)(OS管道缓冲区提供容限)。 – jfs

0

您可能必须明确关闭stdin,这样子进程才会停止,我认为这是您的代码正在发生的情况 - 可以通过在终端上运行top并检查randomnumber的状态是否保持睡眠,并且如果它在预计的执行时间之后使用0%的CPU。

总之,如果您在rng=subprocess(...)呼叫之后立即添加rng.stdin.close(),它可能会继续执行而不会出现问题。另一种选择是做output=rng.communicate(stdin="%d\n" % i)并分别查看output[0]output[1],他们分别是stdoutstderr。你可以在communicatehere找到相关信息。

+0

哇,我忘记了这个问题的存在。如果你关闭stdin,那么你不能再与进程进行通信,这就是在这种情况下需要的,动态响应输出 –

+0

准确地说。我认为你应该给答案增加一个更新,以便其他读者能够理解悬挂的真正原因,这很可能是他们到达这里的原因。另外,我不介意你是否提高了我的答案:) –

+1

@RyanHaining:你的C++代码不检查stdin上的EOF,因此过早地调用'rng.stdin.close()'会导致你的情况出现无限循环。在这种情况下,我会使用'pexpect'或它的Windows模拟。 – jfs