gpt4 book ai didi

python - 子进程,从 STDOUT 读取时重复写入 STDIN (Windows)

转载 作者:可可西里 更新时间:2023-11-01 09:23:04 24 4
gpt4 key购买 nike

我想从 python 调用外部进程。我正在调用的进程读取输入字符串并给出标记化结果,然后等待另一个输入(如果有帮助,二进制是 MeCab 标记器)。

我需要通过调用此过程来标记数千行字符串。

问题是 Popen.communicate()工作但在给出 STDOUT 结果之前等待进程结束。我不想继续关闭和打开新的子流程数千次。 (而且我不想发送整个文本,将来很容易超过数万行。)

from subprocess import PIPE, Popen

with Popen("mecab -O wakati".split(), stdin=PIPE,
stdout=PIPE, stderr=PIPE, close_fds=False,
universal_newlines=True, bufsize=1) as proc:
output, errors = proc.communicate("foobarbaz")

print(output)

我试过阅读 proc.stdout.read()而不是使用 communicate 但它被 stdin 阻止并且在 proc.stdin.close() 之前不返回任何结果叫做。这又意味着我每次都需要创建一个新流程。

我已经尝试通过类似的问题实现队列和线程,如下所示,但它要么不返回任何东西,因此它停留在 While True 上,或者当我强制 stdin 缓冲区填充时重复发送字符串,一次输出所有结果。

from subprocess import PIPE, Popen
from threading import Thread
from queue import Queue, Empty

def enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(line)
out.close()

p = Popen('mecab -O wakati'.split(), stdout=PIPE, stdin=PIPE,
universal_newlines=True, bufsize=1, close_fds=False)
q = Queue()
t = Thread(target=enqueue_output, args=(p.stdout, q))
t.daemon = True
t.start()

p.stdin.write("foobarbaz")
while True:
try:
line = q.get_nowait()
except Empty:
pass
else:
print(line)
break

也查看了 Pexpect 路由,但它的 windows 端口不支持一些重要的模块(基于 pty 的模块),所以我也无法应用它。

我知道有很多类似的答案,而且我已经尝试了其中的大部分。但我尝试过的任何东西似乎都无法在 Windows 上运行。

编辑:关于我正在使用的二进制文件的一些信息,当我通过命令行使用它时。它运行并标记我给出的句子,直到我完成并强行关闭程序。

(...waits_for_input -> input_recieved -> output -> waits_for_input...)

谢谢。

最佳答案

如果 mecab 使用带有默认缓冲的 C FILE 流,则管道标准输出有一个 4 KiB 缓冲区。这里的想法是,程序可以有效地使用小的、任意大小的读取和写入缓冲区,并且底层标准 I/O 实现处理自动填充和刷新更大的缓冲区。这最大限度地减少了所需的系统调用次数并最大限度地提高了吞吐量。显然,您不希望交互式控制台或终端 I/O 或写入 stderr 时出现这种行为。在这些情况下,C 运行时使用行缓冲或不使用缓冲。

程序可以覆盖此行为,并且某些程序确实具有设置缓冲区大小的命令行选项。例如,Python 具有“-u”(无缓冲)选项和 PYTHONUNBUFFERED 环境变量。如果 mecab 没有类似的选项,则 Windows 上没有通用的解决方法。 C运行时的情况太复杂了。 Windows 进程可以静态或动态链接到一个或多个 CRT。 Linux 上的情况不同,因为 Linux 进程通常将单个系统 CRT(例如 GNU libc.so.6)加载到全局符号表中,这允许 LD_PRELOAD 库配置 C FILE 流。 Linux stdbuf 使用这个技巧,例如stdbuf -o0 mecab -O wakati


一个试验选项是调用 CreateConsoleScreenBuffer 并从 msvcrt.open_osfhandle 获取句柄的文件描述符。然后将其作为 stdout 传递,而不是使用管道。子进程会将其视为 TTY 并使用行缓冲而不是全缓冲。然而,管理这一点并非易事。这将涉及读取(即 ReadConsoleOutputCharacter)由另一个进程主动写入的滑动缓冲区(调用 GetConsoleScreenBufferInfo 以跟踪光标位置)。这种互动不是我曾经需要甚至尝试过的。但是我非交互地使用了控制台屏幕缓冲区,即在 child 退出后读取缓冲区。这允许从直接写入控制台而不是 stdout 的程序读取多达 9,999 行输出,例如调用 WriteConsole 或打开“CON”或“CONOUT$”的程序。

关于python - 子进程,从 STDOUT 读取时重复写入 STDIN (Windows),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42991214/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com