Java读取进程输出无缓冲/实时
我想在进程生成时读取它的标准输出。 该过程将发送进度指示器的信息,因此我一次获取所有信息是没有意义的,我这样做了,这就是问题所在。我试着按照一篇文章中的建议使用Scanner类,但我仍然只有在过程完成后才能得到输出。 我知道以前有人问过这个问题,但还没有得到回答 您可能需要首先查看StreamGobblerOutput类Java读取进程输出无缓冲/实时,java,process,progress-bar,stdout,Java,Process,Progress Bar,Stdout,我想在进程生成时读取它的标准输出。 该过程将发送进度指示器的信息,因此我一次获取所有信息是没有意义的,我这样做了,这就是问题所在。我试着按照一篇文章中的建议使用Scanner类,但我仍然只有在过程完成后才能得到输出。 我知道以前有人问过这个问题,但还没有得到回答 您可能需要首先查看StreamGobblerOutput类 public List<String> executeCall(String fileName) { StringBuilder sbOu
public List<String> executeCall(String fileName)
{
StringBuilder sbOutput = new StringBuilder();
StringBuilder sbError = new StringBuilder();
File file = new File(fileName);
try ( BufferedReader br = new BufferedReader(new FileReader(file)) ) {
String line;
while ((line = br.readLine()) != null) {
String [] parts = line.split("\\s");
if(parts.length<2) {
sbError.append("Command too short for call: " + parts[0]);
continue;
}
List<String> args = new ArrayList<String>();
args.add ("sfb.exe");
for(int i = 1; i <parts.length; ++i) {
args.add (parts[i]);
}
args.add (sfbPassword);
ProcessBuilder pb = new ProcessBuilder (args);
pb.directory(new File(Support.getJustThePathFromFile(file)));
Map<String, String> envs = pb.environment();
String path = envs.get("Path");
envs.put("Path", Paths.get(".").toAbsolutePath().normalize().toString() + ";" +path);
//pb.redirectOutput(new Redirect() {});
Process p = pb.start();
String outputPathPrefix = pb.directory().getCanonicalPath();
// any output?
StreamGobblerOutput outputGobbler = new StreamGobblerOutput(p.getInputStream(), outputPathPrefix);
outputGobbler.start();
// any errors?
StreamGobblerError errorGobbler = new StreamGobblerError(p.getErrorStream());
errorGobbler.start();
try
{
p.waitFor();
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
sbOutput = outputGobbler.getOutput();
sbError = errorGobbler.getErrors();
String rootPath= Support.getJustThePathFromFile(new File(fileName));
File rootFile = new File(rootPath + "/..");
String rootFolder = rootFile.getCanonicalFile().getName();
System.err.println("rootFolder: " + rootFolder);
mainApp.addModifiedFiles(outputGobbler.getModifiedFileNames(), rootFolder);
}
} catch ( IOException ex) {
sbError.append(ex.getMessage());
}
mainApp.addOutput(sbOutput.toString());
mainApp.addError(sbError.toString());
return;
}
private class StreamGobblerOutput extends Thread {
private InputStream is;
private String outputPathPrefix;
private StringBuilder sbOutput;
private List<String> modifiedFileNames;
private Scanner scanner;
private StreamGobblerOutput(InputStream is, String outputPathPrefix) {
this.is = is;
this.outputPathPrefix = outputPathPrefix;
sbOutput = new StringBuilder();
modifiedFileNames = new ArrayList<String>();
scanner = new Scanner(is);
}
public StringBuilder getOutput() {
return sbOutput;
}
public List<String> getModifiedFileNames() {
return modifiedFileNames;
}
@Override
public void run() {
//create pattern
Pattern patternProgress = Pattern.compile("\\((\\d+)%\\)");
//InputStreamReader isr = new InputStreamReader(is);
//BufferedReader br = new BufferedReader(isr);
String ligne = null;
while (scanner.hasNextLine()) {
ligne = scanner.nextLine();
sbOutput.append(ligne);
sbOutput.append("\r\n");
//bw.write("\r\n");
Matcher mProgress = patternProgress.matcher(ligne);
if (mProgress.find()) {
int percentage = Integer.parseInt(mProgress.group(1));
System.err.println("percentage=" + percentage);
mainApp.mainWindowController.setProgressExecute(percentage/100.0);
}
}
mainApp.mainWindowController.setProgressExecute(1.0);
if (scanner != null) {
scanner.close();
}
}
}
private class StreamGobblerError extends Thread {
private InputStream is;
private StringBuilder sbError;
private Scanner scanner;
private StreamGobblerError(InputStream is) {
this.is = is;
sbError = new StringBuilder();
scanner = new Scanner(is);
}
public StringBuilder getErrors() {
return sbError;
}
@Override
public void run() {
//InputStreamReader isr = new InputStreamReader(is);
//BufferedReader br = new BufferedReader(isr);
String ligne = null;
while (scanner.hasNextLine()) {
ligne = scanner.nextLine();
sbError.append(ligne);
sbError.append("\r\n");
}
if (scanner != null) {
scanner.close();
}
}
}
public List executeCall(字符串文件名)
{
StringBuilder sbOutput=新的StringBuilder();
StringBuilder sbError=新建StringBuilder();
文件=新文件(文件名);
try(BufferedReader br=new BufferedReader(new FileReader(file))){
弦线;
而((line=br.readLine())!=null){
String[]parts=line.split(\\s”);
如果(parts.length如果您的外部进程基于C/C++(stdio),那么这很可能是块缓冲问题:
如果基于stdio的程序在终端中以交互方式运行,则通常会进行行缓冲,当其stdout重定向到管道时,会进行块缓冲。在后一种情况下,在缓冲区溢出或刷新之前,您不会看到新行
有关更多详细信息和一些可能的解决方法,请参阅
还请注意,根据,Win32上不提供行缓冲:
\u IOLBF
对于某些系统,这提供了行缓冲。但是,对于Win32,其行为与_IOFBF-完全缓冲相同
因此,如果您选择修改“exe”程序以使用setvbuf
设置正确的输出模式,则必须使用:
_IONBF No buffer
相反。来自Javadocs
可选地,可以创建打印流以便自动刷新;
这意味着flush方法将在一个字节后自动调用
数组,调用一个println方法,或换行
已写入字符或字节('\n')
参考:
一种方法是在每次写入后让输出流flush()。
System.out.flush()
您还可以定义自己的可冲洗打印流并使用它
您有权访问生成输出的进程的代码吗?也许您可以在某个时间间隔/在n次写入之后在那里进行刷新?我没有权限访问我调用的进程的代码,但当我从控制台调用它时,它似乎会一个接一个地输出进度指标数据,即:似乎在每个数据之后都会刷新点。@Adder-您调用的外部程序是基于C/C++的吗?您可以将exe输出到一个文件中,然后跟踪该文件以检查是否逐个输出进度指示器?我尝试将其重定向到文件中,但当我读取该文件时,我再次遇到缓冲问题。谢谢您的回答。C程序需要如何修改?这是y、 我猜。@Adder,是的,如果这是windows二进制文件,请参阅此链接:,但请注意,win32下不支持行缓冲。我生成数据点的过程不是用java编写的,只能间接访问。。