Java 格式化输入流的正确方法

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

我有以下问题:我的程序被传递了一个InputStream,我无法控制它的内容。我使用javax库对输入流进行解组,如果输入流包含&字符而不是后跟“amp;”,则该库正确地抛出异常

我提出的解决方法是创建以下类:

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 &amp; Bar", "Foo & Bar");
          test("&amp;&amp;&amp;", "&&&");
          test("&amp;&amp;&amp; ", "&&& ");
          test(" &amp;&amp;&amp;", " &&&");
          test("&amp;", "&");
          test("&amp;", "&amp;");
          test("&amp;&amp;", "&amp;&amp;");
          test("&amp;&amp;&amp;", "&amp;&&amp;");
          test("test", "test");
          test("", "");
          test("testtesttest&amp;", "testtesttest&");