Java 格式化输入流的正确方法
我有以下问题:我的程序被传递了一个InputStream,我无法控制它的内容。我使用javax库对输入流进行解组,如果输入流包含&字符而不是后跟“amp;”,则该库正确地抛出异常 我提出的解决方法是创建以下类:Java 格式化输入流的正确方法,java,formatting,inputstream,super,Java,Formatting,Inputstream,Super,我有以下问题:我的程序被传递了一个InputStream,我无法控制它的内容。我使用javax库对输入流进行解组,如果输入流包含&字符而不是后跟“amp;”,则该库正确地抛出异常 我提出的解决方法是创建以下类: import java.io.ByteArrayInputStream; import java.io.FilterInputStream; import java.io.InputStream; /** * Provide an input stream where all &am
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.InputStream;
/**
* Provide an input stream where all & characters are properly encoded as &
*/
public class FormattedStream extends FilterInputStream {
public FormattedStream(InputStream src) {
super(new ByteArrayInputStream(StringUtil.toString(src)
.replace("&", "&").replace("amp;amp;", "amp;").getBytes()));
}
}
注意:StringUtil是一个简单的实用工具,我必须将输入流转换为字符串
该类就绪后,我现在使用以下命令调用JAXB解组器:
unmarshal(new FormattedStream(inputStream));
而不是
unmarshal(inputStream);
这种方法是可行的,但出于以下几个原因,它确实显得有些奇怪:
1-由于super必须是构造函数中的第一个元素这一限制(尽管我读到了关于它的内容,但我还是无法理解这一限制),我被迫在一行中完成所有处理,这使得代码很难可读
2-将整个流转换为字符串并返回到流似乎有些过分
3-上述代码稍有错误,因为包含amp;amp;将修改为包含amp
我可以通过提供一个FormatInputStream类和一个方法来解决1:
InputStream preProcess(InputStream inputStream)
我将在FormattedStream类的构造函数中执行与当前相同的操作,但由于编码限制,必须选择不同的接口似乎有些奇怪
我可以通过保持FormattedStream构造函数的简单性来解决2:
super(src)
重写三个read方法,但这将涉及更多的编码:通过替换&on-the-fly来重写三个read方法,与我目前拥有的一行代码相比,这一行代码可以利用replaceAll-String方法
至于第三,这似乎是一个角落的情况,我不担心它,但也许我应该
有没有关于如何以更优雅的方式解决我的问题的建议?为了避免将所有数据读入RAM,您可以实现一个
FilterInputStream
(您必须重写read()
和read(byte[],int,int)
,并以某种方式缓冲这些额外的字节。这不会导致代码变短
真正的解决方案是修复无效的数据源(如果要实现自动化,需要考虑编写自己的XML解析器) 你的方法有一些缺陷
的结果取决于系统;它也是一种转码操作,可能与String.getBytes()
所做的任何事情都不对称-许多系统上的默认编码是。您应该使用StringUtil.toString
- 这样的全局搜索和替换可能会损坏您的文档-安培数可能存在于
- 我同意麦克道尔的回答,即最重要的是首先修复无效的数据源
无论如何,这里有一个
InputStream
,它查找孤独的&
字符,并将它们与一个额外的amp;
结合,以防丢失。同样,用这种方法修复损坏的数据在大多数情况下都没有效果
此解决方案修复了OP中提到的三个缺陷,并且只显示了实现转换InputStreams的一种方法
- 在构造函数中,只保留对原始InputStream的引用。构造函数中不进行任何处理,直到流真正被请求数据(通过调用read())
- 内容未转换为大型单个字符串进行转换。相反,流作为流工作,只执行最小的预读(例如,确定
后面是否紧跟&
所需的四个字节)amp;
- 流只替换孤独的
,并且不会以任何方式尝试清理&
,因为这种解决方案不会发生这种情况amp;amp;
这很好:谢谢你的例子。但是有一个问题:不管流是如何访问的,你不需要重写read(byte[],int,int)方法来让代码工作吗?顺便说一句,我找到了问题的根源,并试图在那里修复它。
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
public class ReplacerInputStream extends InputStream {
private static final byte[] REPLACEMENT = "amp;".getBytes();
private final byte[] readBuf = new byte[REPLACEMENT.length];
private final Deque<Byte> backBuf = new ArrayDeque<Byte>();
private final InputStream in;
public ReplacerInputStream(InputStream in) {
this.in = in;
}
@Override
public int read() throws IOException {
if (!backBuf.isEmpty()) {
return backBuf.pop();
}
int first = in.read();
if (first == '&') {
peekAndReplace();
}
return first;
}
private void peekAndReplace() throws IOException {
int read = super.read(readBuf, 0, REPLACEMENT.length);
for (int i1 = read - 1; i1 >= 0; i1--) {
backBuf.push(readBuf[i1]);
}
for (int i = 0; i < REPLACEMENT.length; i++) {
if (read != REPLACEMENT.length || readBuf[i] != REPLACEMENT[i]) {
for (int j = REPLACEMENT.length - 1; j >= 0; j--) {
// In reverse order
backBuf.push(REPLACEMENT[j]);
}
return;
}
}
}
}
test("Foo & Bar", "Foo & Bar");
test("&&&", "&&&");
test("&&& ", "&&& ");
test(" &&&", " &&&");
test("&", "&");
test("&", "&");
test("&&", "&&");
test("&&&", "&&&");
test("test", "test");
test("", "");
test("testtesttest&", "testtesttest&");