简体   繁体   English

如何将 Java 控制台输出读入字符串缓冲区

[英]How could I read Java Console Output into a String buffer

I have a Java program that outputs some text into console.我有一个将一些文本输出到控制台的 Java 程序。 It uses print , println , and some other methods to do this.它使用printprintln和其他一些方法来做到这一点。

At the end of the program , I want to read all the text in console and copy it into a String buffer.在程序结束时,我想读取控制台中的所有文本并将其复制到字符串缓冲区中。 How could I do this in Java ?我怎么能在 Java 中做到这一点? I need to read stdout and stderr separately.我需要分别读取stdoutstderr

Ok, this was a fun problem.好吧,这是一个有趣的问题。 Dosen't seem to be an elegant way of solving it for all PrintStream methods at once.似乎不是同时解决所有PrintStream方法的优雅方法。 (Unfortunately there is no FilterPrintStream .) (不幸的是没有FilterPrintStream 。)

I did write up an ugly reflection-based workaround though (not to be used in production code I suppose :)我确实写了一个丑陋的基于反射的解决方法(我想不会在生产代码中使用:)

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;
    }
}

...that can be used like this: ...可以这样使用:

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);
    }
}

Resulting output:结果输出:

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

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

(Note though, that the out field in FilterOutputStream is protected and documented, so it is part of the API :-) (但请注意, FilterOutputStream中的out字段受到保护和记录,因此它是 API 的一部分 :-)

You can't do that once the program is finished running.一旦程序完成运行,你就不能这样做。 You need to do it before the program starts to write output.您需要在程序开始写入输出之前执行此操作。

See this article (archive.org) for details on how to replace stdout and stderr.有关如何替换 stdout 和 stderr 的详细信息,请参阅这篇文章(archive.org) The core calls are System.setOut() and System.setErr() .核心调用是System.setOut()System.setErr()

You can use PipedInputStream and PipedOutputStream.您可以使用 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

Here is a utility Class named ConsoleOutputCapturer.这是一个名为 ConsoleOutputCapturer 的实用程序类。 It allows the output to go to the existing console however behind the scene keeps capturing the output text.它允许输出到现有的控制台,但在幕后不断捕获输出文本。 You can control what to capture with the start/stop methods.您可以使用开始/停止方法控制要捕获的内容。 In other words call start to start capturing the console output and once you are done capturing you can call the stop method which returns a String value holding the console output for the time window between start-stop calls.换句话说,调用 start 开始捕获控制台输出,一旦完成捕获,您就可以调用 stop 方法,该方法返回一个字符串值,该值保存开始-停止调用之间的时间窗口的控制台输出。 This class is not thread-safe though.虽然这个类不是线程安全的。

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(); } } } }

These two line of code will put your output in a text file or u can change the destination as u require.这两行代码会将您的输出放在一个文本文件中,或者您可以根据需要更改目的地。

// Create a file: System.setOut(new PrintStream( new FileOutputStream("D:/MyOutputFile.txt"))); // 创建一个文件: System.setOut(new PrintStream( new FileOutputStream("D:/MyOutputFile.txt"))); // Redirect the output to the file: System.out.println("Hello to custom output stream!"); // 将输出重定向到文件: System.out.println("Hello to custom output stream!");

hope its help u .. :)希望它对你有帮助.. :)

之后不要这样做,在调用第一个System.out.print()之前创建两个StringBuilder对象,只需将要保存的每个字符串附加到适当的StringBuilder

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM