【问题标题】:Java OutputStream reading lines of stringsJava OutputStream 读取字符串行
【发布时间】:2019-10-19 22:12:14
【问题描述】:

我使用一个接受 OutputStream 的 API(有关更多信息,请参见下文)来捕获数据。相反,我想提供一个 ConsumerStrings 一行一行地使用数据。因此,我必须编写一个包装了这样一个消费者的 OutputStream 实现。这是我能想到的最简单的:

import java.io.OutputStream;
import java.util.Objects;
import java.util.function.Consumer;

public class OutputStreamConsumer extends OutputStream {

    private final Consumer<String> consumer;
    private final StringBuilder sb = new StringBuilder();

    public OutputStreamConsumer(Consumer<String> consumer) {
        this.consumer = Objects.requireNonNull(consumer);
    }

    @Override
    public void write(int b) {
        char c = (char) b;
        if (c == '\r') {
            return;
        }
        if (c == '\n') {
            consume();
            return;
        }
        this.sb.append(c);
    }

    @Override
    public void close() {
        if (sb.length() != 0) {
            consume();
        }
    }

    private void consume() {
        this.consumer.accept(this.sb.toString());
        this.sb.delete(0, Integer.MAX_VALUE);
    }

}

但是,就编码性能而言,这对于生产代码来说可能还不够。我认为必要的逻辑已经包含在 InputStreamReaderBufferedReader 中,但我不能在这种情况下使用这些类。 编写这种 OutputStream 的最佳方法是什么?我可以使用哪些 jdk 类来避免编写一堆低级代码处理编码、行尾等。

具体用例

在 Gradle 插件项目中,我使用 Gradle 的项目 API:ExecSpec 启动了一个外部进程。在那里,我可以使用 setStandardOutputsetErrorOutput 方法设置 OutputStreams,以便捕获进程的输出。

【问题讨论】:

    标签: java character-encoding outputstream


    【解决方案1】:

    由于到目前为止还没有答案,我将在撰写本文时发布我的“最佳”解决方案。随时发布更好的解决方案。

    import java.io.IOException;
    import java.io.OutputStream;
    import java.nio.charset.StandardCharsets;
    import java.util.Arrays;
    import java.util.Objects;
    import java.util.function.Consumer;
    
    public class LineReadingOutputStream extends OutputStream {
    
        private static final byte CR = '\r';
        private static final byte LF = '\n';
    
        private final Consumer<String> consumer;
        private final StringBuilder stringBuilder = new StringBuilder();
        private boolean lastCR = false;
    
        private LineReadingOutputStream(final Consumer<String> consumer) {
            this.consumer = Objects.requireNonNull(consumer);
        }
    
        @Override
        public void write(final int b) throws IOException {
            write(new byte[]{(byte) b});
        }
    
        @Override
        public void write(final byte[] b, int start, final int len) {
            if (b == null) {
                throw new NullPointerException();
            }
            if (len < 0) {
                throw new IllegalArgumentException();
            }
            final int end = start + len;
            if ((start < 0) || (start > b.length) || (end < 0) || (end > b.length)) {
                throw new IndexOutOfBoundsException();
            }
    
            if (this.lastCR && start < end && b[start] == LF) {
                start++;
                this.lastCR = false;
            } else if (start < end) {
                this.lastCR = b[end - 1] == CR;
            }
    
            int base = start;
            for (int i = start; i < end; i++) {
                if (b[i] == LF || b[i] == CR) {
                    final String chunk = asString(b, base, i);
                    this.stringBuilder.append(chunk);
                    consume();
                }
                if (b[i] == LF) {
                    base = i + 1;
                } else if (b[i] == CR) {
                    if (i < end - 1 && b[i + 1] == LF) {
                        base = i + 2;
                        i++;
                    } else {
                        base = i + 1;
                    }
                }
            }
            final String chunk = asString(b, base, end);
            this.stringBuilder.append(chunk);
        }
    
        @Override
        public void close() {
            if (this.stringBuilder.length() != 0) {
                consume();
            }
        }
    
        private static String asString(final byte[] b, final int start, final int end) {
            if (start > end) {
                throw new IllegalArgumentException();
            }
            if (start == end) {
                return "";
            }
            final byte[] copy = Arrays.copyOfRange(b, start, end);
            return new String(copy, StandardCharsets.UTF_8);
        }
    
        private void consume() {
            this.consumer.accept(this.stringBuilder.toString());
            this.stringBuilder.delete(0, Integer.MAX_VALUE);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-11-03
      • 2010-11-30
      • 2013-07-20
      • 2015-03-05
      • 2014-11-25
      • 2021-01-01
      • 1970-01-01
      • 2010-11-08
      相关资源
      最近更新 更多