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

问题描述 投票:0回答:7

我有一个 Java 程序可以将一些文本输出到控制台。它使用

print
println
和其他一些方法来做到这一点。

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

stdout
stderr

java console
7个回答
17
投票

好的,这是一个有趣的问题。似乎不是一次解决所有

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 的一部分:-)


6
投票

一旦程序完成运行,你就不能这样做。您需要在程序开始写入输出之前执行此操作。

参见这篇文章(archive.org)了解如何替换stdout和stderr的详细信息。核心调用是

System.setOut()
System.setErr()
.


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

1
投票

这里是一个名为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(); } } } }

0
投票

之后不要这样做,在调用第一个

StringBuilder
之前创建两个
System.out.print()
对象,然后将要保存的每个字符串附加到适当的
StringBuilder
.


0
投票

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

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

希望它能帮助你.. :)


0
投票

通过实现模式装饰器和设置简单的决定

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
© www.soinside.com 2019 - 2024. All rights reserved.