子进程 readline 挂起等待 EOF

发布于 2024-12-12 07:51:18 字数 1469 浏览 0 评论 0原文

我有一个简单的 C++ 程序,我试图通过 python 脚本执行它。 (我对编写脚本非常陌生)并且我在通过管道读取输出时遇到问题。从我所看到的来看,如果没有 EOF,readline() 似乎无法工作,但我希望能够在程序中间读取并使脚本响应输出的内容。它不读取输出,而是挂起 python脚本:

#!/usr/bin/env python
import subprocess
def call_random_number():
    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()

call_random_number()

和c++文件,它生成1到100之间的随机数,然后检查用户的猜测,直到他们猜对为止,

#include<iostream>
#include<cstdlib>

int main(){
  std::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\n";
  std::srand(std::time(NULL));
  int num = std::rand() % 100;
  int guessCount = 0;
  int guess = -1;
  std::cout << "Please enter a number:  ";
  std::cin >> guess;
  while(guess != num){
    if (guess > num){
        std::cout << "That guess is too high.  Please guess again:  ";
    } else {
        std::cout << "That guess is too low.  Please guess again:  ";
    }
    std::cin >> guess;
    guessCount++;
  }
  std::cout << "Congratulations!  You solved it in " << guessCount << " guesses!\n";
}

最终目标是让脚本通过二分搜索解决问题,但现在我只想能够读取一行而不是文件末尾

I have a simple c++ program that I'm trying to execute through a python script. (I'm very new to writing scripts) and I'm having trouble reading output through the pipe. From what I've seen, it seems like readline() won't work without EOF, but I want to be able to read in the middle of the program and have the script respond to whats being outputted. Instead of reading output, it just hangs
the python script:

#!/usr/bin/env python
import subprocess
def call_random_number():
    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()

call_random_number()

and the c++ file, which generates a random number between one and 100, then checks the users guess until they guess correctly

#include<iostream>
#include<cstdlib>

int main(){
  std::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\n";
  std::srand(std::time(NULL));
  int num = std::rand() % 100;
  int guessCount = 0;
  int guess = -1;
  std::cout << "Please enter a number:  ";
  std::cin >> guess;
  while(guess != num){
    if (guess > num){
        std::cout << "That guess is too high.  Please guess again:  ";
    } else {
        std::cout << "That guess is too low.  Please guess again:  ";
    }
    std::cin >> guess;
    guessCount++;
  }
  std::cout << "Congratulations!  You solved it in " << guessCount << " guesses!\n";
}

the eventual goal is to have the script solve the problem with a binary search, but for now I just want to be able to read a line without it being the end of the file

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

素罗衫 2024-12-19 07:51:18

正如 @Ron Reiter 指出的,您不能使用 readline() 因为 cout 不会隐式打印换行符 - 您在这里需要 std::endl"\n"

对于交互式使用,当您无法更改子程序时,pexpect module 提供了几种方便的方法(一般来说它解决了免费:直接从终端输入/输出(在标准输入/标准输出之外)和块缓冲问题):

#!/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)

它可以工作,但它是二进制搜索,因此 (传统上)可能存在错误

下面是使用 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())

As @Ron Reiter pointed out, you can't use readline() because cout doesn't print newlines implicitly -- you either need std::endl or "\n" here.

For an interactive use, when you can't change the child program, pexpect module provides several convenience methods (and in general 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)

It works but it is a binary search therefore (traditionally) there could be bugs.

Here's a similar code that uses subprocess module for comparison:

#!/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())
玩物 2024-12-19 07:51:18

我非常确定在 C++ 程序中添加换行符会导致读取行返回。

I'm pretty sure adding newlines in your C++ program will cause the readlines to return.

岁月无声 2024-12-19 07:51:18

您可能必须显式关闭stdin,以便子进程将停止挂起,我认为这就是您的代码所发生的情况 - 这可以通过在终端上运行 top 并检查是否 randomnumber 的状态保持睡眠状态,如果它在执行所需的预期时间之后使用 0% CPU。

简而言之,如果您在 rng=subprocess(...) 调用之后添加 rng.stdin.close(),它可能会毫无问题地恢复。另一种选择是执行 output=rng.communicate(stdin="%d\n" % i) 并查看 output[0]output[ 1]分别是stdoutstderr。您可以在此处<找到有关沟通的信息< /a>.

You may have to explicitly closestdin, so the child process will stop hanging, which I think is what is happening with your code -- this can be verified by running top on a terminal and checking if randomnumber's status stays sleeping and if it is using 0% CPU after the expected time it would take to execute.

In short, if you add rng.stdin.close() right after the rng=subprocess(...) call, it may resume with no problem. Another option would be to do output=rng.communicate(stdin="%d\n" % i)and look at output[0]andoutput[1]who are stdout and stderr, respectively. You can find info oncommunicate here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文