我有一个 Java 程序可以将一些文本输出到控制台。它使用
print
、println
和其他一些方法来做到这一点。
程序结束时,我想读取控制台中的所有文本并将其复制到字符串缓冲区中。我怎么能在 Java 中做到这一点?我需要分别阅读
stdout
和stderr
。
好的,这是一个有趣的问题。似乎不是一次解决所有
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
(但请注意,
out
中的 FilterOutputStream
字段受到保护和记录,因此它是 API 的一部分:-)
一旦程序完成运行,你就不能这样做。您需要在程序开始写入输出之前执行此操作。
参见这篇文章(archive.org)了解如何替换stdout和stderr的详细信息。核心调用是
System.setOut()
和System.setErr()
.
您可以使用 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
这里是一个名为ConsoleOutputCapturer的实用类。它允许输出到现有的控制台,但在幕后不断捕获输出文本。您可以使用启动/停止方法控制要捕获的内容。换句话说,调用 start 开始捕获控制台输出,一旦完成捕获,您可以调用 stop 方法,该方法返回一个 String 值,该值保存 start-stop 调用之间时间窗口的控制台输出。虽然这个类不是线程安全的。
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();
}
}
}
}
之后不要这样做,在调用第一个
StringBuilder
之前创建两个System.out.print()
对象,然后将要保存的每个字符串附加到适当的StringBuilder
.
这两行代码会将您的输出放在一个文本文件中,或者您可以根据需要更改目的地。
// 创建文件: System.setOut(new PrintStream( new FileOutputStream("D:/MyOutputFile.txt"))); // 将输出重定向到文件: System.out.println("Hello to custom output stream!");
希望它能帮助你.. :)
通过实现模式装饰器和设置简单的决定
System.out
& System.err
实施
import java.io.PrintStream;
/** Simple implementation of pattern Decorator. */
public class SystemOutputProxy extends PrintStream {
private final PrintStream printStream;
private final StringBuilder sb;
public SystemOutputProxy(PrintStream printStream, StringBuilder sb) {
super(printStream);
this.printStream = printStream;
this.sb = sb;
}
@Override public void print(Object obj) {
sb.append(obj);
printStream.print(obj);
}
@Override public void print(String x) {
sb.append(x);
printStream.println(x);
}
/* override all others overloading of method: print(...) */
@Override public void println(Object x) {
sb.append(x).append(System.lineSeparator());
printStream.println(x);
}
@Override public void println(String x) {
sb.append(x).append(System.lineSeparator());
printStream.println(x);
}
/* override all others overloading of method: println(...) */
@Override public PrintStream printf(String format, Object... args) {
sb.append(String.format(format, args));
return printStream.printf(format, args);
}
/* override all others overloading of method: printf(...) */
public PrintStream getPrintStream() {
return printStream;
}
}
使用陈列柜
public static void main(String[] args) {
StringBuilder out = new StringBuilder();
StringBuilder err = new StringBuilder();
SystemOutputProxy proxyOut = new SystemOutputProxy(System.out, out);
SystemOutputProxy proxyErr = new SystemOutputProxy(System.err, err);
System.setOut(proxyOut);
System.setErr(proxyErr);
// do your code here...
System.out.print("aaa");
System.out.print("bbb");
System.out.print("ccc");
System.err.print("111");
System.err.print("222");
System.err.print("333");
// finish your code...
// set back original Output is not necessary
System.setOut(proxyOut.getPrintStream());
System.setErr(proxyErr.getPrintStream());
boolean isOurContentEquals = out.toString().equals("aaabbbccc");
boolean isErrContentEquals = err.toString().equals("111222333");
System.out.println("isOurContentEquals = " + isOurContentEquals); // show true
System.out.println("isErrContentEquals = " + isErrContentEquals); // show true
}
控制台展示
aaa
bbb
ccc
isOurContentEquals = true
isErrContentEquals = true
111
222
333