【问题标题】:Run C program with interactive IO using ProcessBuilder使用 ProcessBuilder 运行具有交互式 IO 的 C 程序
【发布时间】:2018-05-18 06:22:28
【问题描述】:

我正在使用java(虽然有很多可用的,但我想要自己的)开发一种用于 C/C++ 的 IDE 软件,它可以编译和执行 C 或 C++ 程序。所以我尝试了一个简单的程序来使用ProcessProcessBuilder.在java中编译和执行C程序

这是我编译和执行 C 程序的简单 java 程序:

public class RunProgram {

    public static void main(String[] args) throws Exception {
        new ProcessBuilder("gcc", "-o", "first", "first.c").start().waitFor(); //To Compile the source file using gcc and wait for compilation


/*
    Although I've to handle error-stream but 
for now, my assumption is that there is no error 
in program.
*/


        ProcessBuilder run = new ProcessBuilder("./first");

        execute.redirectErrorStream(true);

        Process runProcess = run.start();

        StreamReader sr = new StreamReader(runProcess.getInputStream());

        new Thread(sr).start(); //A new thread to handle output of program . 

    //rest of coding to provide input using OutputStream of 'runProcess' and to close the stream.

    }
}

class StreamReader implements Runnable {

    private InputStream reader;

   public StreamReader(InputStream inStream) {
      reader = inStream;

   }

   @Override
   public void run() {

      byte[] buf = new byte[1024];

        int size = 0;
        try {

            while ((size = reader.read(buf)) != -1) {

                System.out.println(new String(buf));
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
   }
}

这是我的first.c 程序。

#include<stdio.h>

int main() {
    int a;
    int k;

    printf("input a: ");

    scanf("%d", &a);
    for(k = 0; k < a; k++)
        printf("k = %d\n", k);
    return 0;
}

我想创建交互式IO 控制台,就像大多数 IDE 或命令终端(基于 Linux 的操作系统中的终端和基于 Windows 的操作系统中的命令提示符)。对于上面的例子:首先,它应该打印“Input a: “然后等待输入被提供,然后是程序的其余部分。但它不会像我想的那样工作,因为它没有' t 打印出现在scanf 之前的printf 语句的结果,直到我可能使用OutputStream 提供输入。

我搜索了我的问题并访问了许多链接,但没有得到解决方案。同时,我发现this 链接建议在每个printf 语句之后附加fflush 或使用setbufsetvbuf 方法(来自其他一些子链接)来清除缓冲区。但是一个新人(即将学习 C)可能不知道fflush 或这些功能,他/她永远不会使用它,因为它在其他 IDE 中不需要,甚至在终端上也不需要。 如何解决这个问题并为我的 IDE 构建集成控制台

这是我想要的一瞥:

【问题讨论】:

  • 我的这个answer 更深入地解释了幕后发生的事情以及为什么应该在被调用程序中处理它。我能想象的唯一选择是 Unix-Linux 中的伪终端,但它是一种相当先进(且不可移植)的方式。
  • @SergeBallesta 我看过你的回答,但我不明白你的回答将如何帮助我解决这个问题。
  • 不幸的是,没有办法解决它(除了伪终端)......
  • @SergeBallesta eclipse 和其他 IDE 也使用伪终端

标签: java process io processbuilder


【解决方案1】:

从上面的 cmets 中,认为在这里添加一点关于 I/O 流缓冲如何工作的解释是有意义的。

当调用printf(3) 等时,幕后发生的事情是数据被写入缓冲区,直到缓冲区填满或发生某些触发。 然后将缓冲区的内容从缓冲区复制到实际的输出设备/另一个输出缓冲区... 触发器通常遇到行尾(Linux/Unix 下的\n)。 因此,这种缓冲的粗略版本是:

struct buffered_file_t {

    char* buffer;
    size_t capacity;
    size_t current_char;
    FILE* file;

};

void flush_buffered(struct buffered_file_t* file) {

    assert(0 != file);
    assert(0 != file->buffer);

    fwrite(file->buffer, file->current_char, 1, file->file);
    file->current_char = 0;

}


void print(struct buffered_file_t* file, const char* str) {

    assert(0 != file);
    assert(0 != file->buffer);
    assert(0 != str);

    for(size_t i = 0;  0 != str[i]; ++i) {

        if(file->current_char >= file->capacity - 1) flush_buffered(file);
        file->buffer[file->current_char++] = str[i];
        if('\n' == str[i]) flush_buffered(file);

    }

}

现在,如果你调用print

const size_t BUFSIZE = 100;

struct buffered_file_t stdout_buffered = {
    .buffer = calloc(1, BUFSIZE),
    .capacity = BUFSIZE,
    .current_char = 0,
    .file= stdout,
};

print(&stdout_buffered, "Naglfar\n");
print(&stdout_buffered, "Surthur");

您永远不会看到Surthur 出现在stdout 上。 为了让它从缓冲区写入stdout,你必须要么

  • 显式调用flush_buffered
  • 通过减小缓冲区大小来禁用缓冲(上例中为buffered_file.capacity = 1

在您的情况下,您不能显式调用fflush(3)(这就是您所说的要求)。因此剩下的唯一方法就是禁用缓冲。

如何做到这一点取决于操作系统,恕我直言。 对于 Linux,请查看 Coreutils 包中的 stdbuf(1),了解如何为某些外部进程流禁用缓冲。

【讨论】:

    【解决方案2】:

    在 GNU/Linux 下,要关闭标准 I/O 流的缓冲,您可以使用 stdbuf(1),如下所示:

     ....
     ProcessBuilder run = new ProcessBuilder("stdbuf", "-o0", "./first");
     ....
    

    如果您还想关闭 stderrstdin 的缓冲,请添加 -e0-i0 选项。

    当然,如果您不必依赖外部工具而可以在自己的代码中关闭缓冲,那就更好了 - 最简单的方法是查看 stdbuf 的源代码,但我猜是最终会导致您不得不使用 JNI,然后,我想,我会坚持使用 stdbuf ...

    【讨论】:

    • 我试过你的代码,但它产生了异常:can't run program 'stdbuf' as studbuf is not an executable program
    • 当然,stdbuf 必须在路径中 - 安装 coreutils。我的目的是让你先看看,如果你打算坚持使用 stdbuf 来解决你的问题,我建议将它与你的软件一起分发。
    • Bear 其实我不想坚持stdbuf,而是想要一个解决方案,所以我正在尝试一切。由于我在互联网上搜索了很多,我没有找到任何第一步解决方案
    • 从您的角度来看,我必须将它与我的 IDE 一起分发,这有点麻烦。而且我认为,将缓冲区设置为 0 并不是一个好习惯,因为程序使用缓冲区来固定执行和其他目的。
    • 你好,我想回复两点:正如我所说,我打算给你stdbuf作为开始寻找的提示。你知道C,所以看看它的源代码。
    猜你喜欢
    • 2020-10-04
    • 1970-01-01
    • 2014-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-30
    • 1970-01-01
    相关资源
    最近更新 更多