Java调用C程序时,如何让InputStream先于OutputStream读取?
Java调用C程序时输出顺序异常的原因及解决办法
问题根源
C程序的标准输出(stdout)缓冲规则会根据输出目标自动切换:
- 直接输出到终端时,默认是行缓冲,碰到换行符
\n就立刻把缓冲区内容打出来,所以"name?"会马上显示。 - 但当stdout被重定向到Java进程的管道(也就是Java的InputStream)时,缓冲模式会变成全缓冲——这时候输出内容会先存在缓冲区里,要么等缓冲区填满,要么等程序结束,要么主动触发刷新,才会把内容传给Java。
你的C程序里printf("name?\n")虽然带了换行,但因为是全缓冲模式,内容一直留在C的缓冲区里没发出去。直到你输入完内容,C程序跑完后续代码,进程快结束时才刷新缓冲区,结果就是"name?"和"hi Tea"被Java一次性读出来,导致顺序错乱。
另外,Java如果没及时读取输入流,也会加重缓冲堆积,但核心问题还是C的缓冲机制。
解决办法
1. 修改C程序,强制刷新输出
在printf("name?\n")之后加一行fflush(stdout),强制把缓冲区内容发出去,不管当前是什么缓冲模式:
printf("name?\n"); fflush(stdout); // 立刻刷新输出缓冲区 char name[20]; scanf("%s", name); printf("hi %s\n", name);
2. 修改C程序的缓冲模式
在程序开头直接把stdout设为行缓冲模式,哪怕输出到管道也保持行缓冲:
setvbuf(stdout, NULL, _IOLBF, 0); // 设置标准输出为行缓冲 printf("name?\n"); char name[20]; scanf("%s", name); printf("hi %s\n", name);
3. 优化Java端的读取逻辑
Java要启动独立线程及时读取C程序的标准输出和错误输出,避免因为流阻塞导致进程卡壳,同时确保输入能及时传给C程序:
Process process = Runtime.getRuntime().exec("c program"); // 专门线程读C程序的标准输出 new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 专门线程读C程序的错误输出(防止错误输出堆积阻塞进程) new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { String line; while ((line = reader.readLine()) != null) { System.err.println(line); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 处理用户输入,传给C程序 try (PrintWriter writer = new PrintWriter(process.getOutputStream(), true)) { Scanner scanner = new Scanner(System.in); String input = scanner.nextLine(); writer.println(input); } catch (IOException e) { e.printStackTrace(); } process.waitFor();
这里PrintWriter的第二个参数设为true,会自动刷新输出缓冲区,保证输入能立刻传给C程序。
内容的提问来源于stack exchange,提问作者Tea
相关产品推荐
相关产品推荐

