2010-12-02 57 views
12

我有一个Java程序,输出一些文本到控制台。它使用print,println和其他一些方法来做到这一点。我怎么能读Java控制台输出到字符串缓冲区

在程序结束时,我想读取控制台中的所有文本并将其复制到字符串缓冲区中。我怎么能在Java中做到这一点?我需要分别阅读stdoutstderr

回答

17

好吧,这是一个有趣的问题。 Dosen似乎是一次解决所有PrintStream方法的优雅方式。 (不幸的是没有FilterPrintStream

我虽然写了一个丑陋的基于反射的解决方法(而不是在生产代码中使用我想:)

class LoggedPrintStream extends PrintStream { 

    final StringBuilder buf; 
    final PrintStream underlying; 

    LoggedPrintStream(StringBuilder sb, OutputStream os, PrintStream ul) { 
     super(os); 
     this.buf = sb; 
     this.underlying = ul; 
    } 

    public static LoggedPrintStream create(PrintStream toLog) { 
     try { 
      final StringBuilder sb = new StringBuilder(); 
      Field f = FilterOutputStream.class.getDeclaredField("out"); 
      f.setAccessible(true); 
      OutputStream psout = (OutputStream) f.get(toLog); 
      return new LoggedPrintStream(sb, new FilterOutputStream(psout) { 
       public void write(int b) throws IOException { 
        super.write(b); 
        sb.append((char) b); 
       } 
      }, toLog); 
     } catch (NoSuchFieldException shouldNotHappen) { 
     } catch (IllegalArgumentException shouldNotHappen) { 
     } catch (IllegalAccessException shouldNotHappen) { 
     } 
     return null; 
    } 
} 

...可以像使用这样的:

public class Test { 
    public static void main(String[] args) { 

     // Create logged PrintStreams 
     LoggedPrintStream lpsOut = LoggedPrintStream.create(System.out); 
     LoggedPrintStream lpsErr = LoggedPrintStream.create(System.err); 

     // Set them to stdout/stderr 
     System.setOut(lpsOut); 
     System.setErr(lpsErr); 

     // Print some stuff 
     System.out.print("hello "); 
     System.out.println(5); 
     System.out.flush(); 

     System.err.println("Some error"); 
     System.err.flush(); 

     // Restore System.out/System.err 
     System.setOut(lpsOut.underlying); 
     System.setErr(lpsErr.underlying); 

     // Print the logged output 
     System.out.println("----- Log for System.out: -----\n" + lpsOut.buf); 
     System.out.println("----- Log for System.err: -----\n" + lpsErr.buf); 
    } 
} 

结果输出:

hello 5 
Some error 
----- Log for System.out: ----- 
hello 5 

----- Log for System.err: ----- 
Some error 

(注寿呃,FilterOutputStream中的out字段是受保护和记录的,因此它是API的一部分:-)

+1

哇!非常感谢。这个对我有用。 – Jacob 2010-12-03 05:49:28

5

一旦程序运行完毕,您就无法做到这一点。您需要在程序开始写入输出之前执行此操作。有关如何替换stdout和stderr的详细信息,请参阅this article。核心电话是System.setOut()System.setErr()

0

不要做之后,在第一个System.out.print()被调用之前创建两个StringBuilder对象,并将您想要保存的每个字符串追加到相应的StringBuilder

1

您可以使用PipedInputStream和PipedOutputStream。

//create pairs of Piped input and output streasm for std out and std err 
final PipedInputStream outPipedInputStream = new PipedInputStream(); 
final PrintStream outPrintStream = new PrintStream(new PipedOutputStream(
    outPipedInputStream)); 
final BufferedReader outReader = new BufferedReader(
    new InputStreamReader(outPipedInputStream)); 
final PipedInputStream errPipedInputStream = new PipedInputStream(); 
final PrintStream errPrintStream = new PrintStream(new PipedOutputStream(
    errPipedInputStream)); 
final BufferedReader errReader = new BufferedReader(
    new InputStreamReader(errPipedInputStream)); 
final PrintStream originalOutStream = System.out; 
final PrintStream originalErrStream = System.err; 
final Thread writingThread = new Thread(new Runnable() { 
    @Override 
    public void run() { 
     try { 
      System.setOut(outPrintStream); 
      System.setErr(errPrintStream); 
      // You could also set the System.in here using a 
      // PipedInputStream 
      DoSomething(); 
      // Even better would be to refactor DoSomething to accept 
      // PrintStream objects as parameters to replace all uses of 
      // System.out and System.err. DoSomething could also have 
      // an overload with DoSomething() calling: 
      DoSomething(outPrintStream, errPrintStream); 
     } finally { 
      // may also want to add a catch for exceptions but it is 
      // essential to restore the original System output and error 
      // streams since it can be very confusing to not be able to 
      // find System.out output on your console 
      System.setOut(originalOutStream); 
      System.setErr(originalErrStream); 
      //You must close the streams which will auto flush them 
      outPrintStream.close(); 
      errPrintStream.close(); 
     } 
    } // end run() 
}); // end writing thread 
//Start the code that will write into streams 
writingThread.start(); 
String line; 
final List<String> completeOutputStreamContent = new ArrayList<String>(); 
while ((line = outReader.readLine()) != null) { 
    completeOutputStreamContent.add(line); 
} // end reading output stream 
final List<String> completeErrorStreamContent = new ArrayList<String>(); 
while ((line = errReader.readLine()) != null) { 
    completeErrorStreamContent.add(line); 
} // end reading output stream 
0

这两行代码会将您的输出置于文本文件中,或者您可以根据需要更改目的地。

//创建一个文件: System.setOut(新的PrintStream(新FileOutputStream中( “d:/MyOutputFile.txt”))); //将输出重定向到文件: System.out.println(“Hello to custom output stream!”);

希望其帮助你.. :)

0

这是一个实用工具类名为ConsoleOutputCapturer。它允许输出到现有控制台,但在场景后面继续捕获输出文本。您可以控制使用启动/停止方法捕获的内容。换句话说,调用start开始捕获控制台输出,一旦完成捕获,您可以调用stop方法,该方法返回一个String值,该值持有控制台输出,用于启动和停止调用之间的时间窗口。这个类虽然不是线程安全的。

import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.util.Arrays; import java.util.List; public class ConsoleOutputCapturer { private ByteArrayOutputStream baos; private PrintStream previous; private boolean capturing; public void start() { if (capturing) { return; } capturing = true; previous = System.out; baos = new ByteArrayOutputStream(); OutputStream outputStreamCombiner = new OutputStreamCombiner(Arrays.asList(previous, baos)); PrintStream custom = new PrintStream(outputStreamCombiner); System.setOut(custom); } public String stop() { if (!capturing) { return ""; } System.setOut(previous); String capturedValue = baos.toString(); baos = null; previous = null; capturing = false; return capturedValue; } private static class OutputStreamCombiner extends OutputStream { private List<OutputStream> outputStreams; public OutputStreamCombiner(List<OutputStream> outputStreams) { this.outputStreams = outputStreams; } public void write(int b) throws IOException { for (OutputStream os : outputStreams) { os.write(b); } } public void flush() throws IOException { for (OutputStream os : outputStreams) { os.flush(); } } public void close() throws IOException { for (OutputStream os : outputStreams) { os.close(); } } } }