我使用一个 API(更多信息,见下文),它接受输出流捕获数据。相反,我想提供一个Consumer of Strings它消耗了一行又一行的数据。因此我必须编写一个 OutputStream 实现来包装这样的 Consumer。这是我能想到的最简单的:
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);
}
}
然而,这对于生产代码来说可能还不够encoding and 表现。我认为必要的逻辑已经包含在输入流读取器 and 缓冲读取器但在这种情况下我不能使用这些类。
编写这种OutputStream的最佳方法是什么?我可以使用哪些 jdk 类来避免编写一堆处理编码、行尾等的低级代码。
具体用例
在 Gradle 插件项目中,我使用 Gradle 的项目 API 启动外部进程:ExecSpec https://docs.gradle.org/current/javadoc/org/gradle/process/ExecSpec.html。我可以使用以下方法设置 OutputStreams设置标准输出 and 设置错误输出为了捕获该过程的输出。
由于到目前为止还没有答案,我将在撰写本文时发布我的“最佳”解决方案。欢迎发布更好的解决方案。
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);
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)