2010-12-02 55 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(); } } } }