【问题标题】:How could I read Java Console Output into a String buffer我如何将 Java 控制台输出读入字符串缓冲区
【发布时间】:2010-12-02 12:08:31
【问题描述】:

我有一个将一些文本输出到控制台的 Java 程序。它使用printprintln 和其他一些方法来执行此操作。

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

【问题讨论】:

    标签: java console


    【解决方案1】:

    好的,这是一个有趣的问题。似乎不是一次解决所有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 的一部分 :-)

    【讨论】:

    • 我无法获得 3 行。从Field ff.get(toLog)。谁能给我解释一下?
    【解决方案2】:

    一旦程序运行完毕,您就不能这样做了。需要在程序开始写输出之前完成。

    有关如何替换 stdout 和 stderr 的详细信息,请参阅this article(archive.org)。核心调用是@987654322@@987654323@

    【讨论】:

      【解决方案3】:

      您可以使用 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
      

      【讨论】:

        【解决方案4】:

        这是一个名为 ConsoleOutputCapturer 的实用程序类。它允许输出到现有的控制台,但在幕后不断捕获输出文本。您可以使用 start/stop 方法控制要捕获的内容。换句话说,调用 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(); } } } }

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

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

            希望对你有所帮助.. :)

            【讨论】:

              猜你喜欢
              • 2013-10-18
              • 2012-04-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-09-21
              • 2023-03-23
              相关资源
              最近更新 更多