【问题标题】:How to convert a Reader to InputStream and a Writer to OutputStream?如何将 Reader 转换为 InputStream 并将 Writer 转换为 OutputStream?
【发布时间】:2010-09-08 21:37:17
【问题描述】:

有没有一种简单的方法可以避免处理文本编码问题?

【问题讨论】:

    标签: java stream


    【解决方案1】:

    这些类的明显名称是 ReaderInputStream 和 WriterOutputStream。不幸的是,这些不包含在 Java 库中。然而,谷歌是你的朋友。

    我不确定它是否能解决所有文本编码问题,这简直就是噩梦。

    There is an RFE, 但它已关闭,不会修复。

    【讨论】:

    • bugs.openjdk.java.net/browse/JDK-4103785 包含评论“我们有一个用于字符集编码的公共 API……没有令人信服的理由来添加这些类”——那么如何在 Java 7 中做到这一点,而无需额外的库,十二年后?
    【解决方案2】:

    您无法真正避免处理文本编码问题,但 Apache Commons 中有现有的解决方案:

    您只需要选择您选择的编码。

    【讨论】:

    【解决方案3】:

    您是否尝试将Reader 的内容写入OutputStream?如果是这样,您将更容易将OutputStream 包装在OutputStreamWriter 中并将chars 从Reader 写入Writer,而不是尝试将阅读器转换为InputStream

    final Writer writer = new BufferedWriter(new OutputStreamWriter( urlConnection.getOutputStream(), "UTF-8" ) );
    int charsRead;
    char[] cbuf = new char[1024];
    while ((charsRead = data.read(cbuf)) != -1) {
        writer.write(cbuf, 0, charsRead);
    }
    writer.flush();
    // don't forget to close the writer in a finally {} block
    

    【讨论】:

      【解决方案4】:

      另外请注意,如果您从 String 开始,您可以跳过创建 StringReader 并使用来自Commons IO 的 org.apache.commons.io.IOUtils 一步创建 InputStream,如下所示:

      InputStream myInputStream = IOUtils.toInputStream(reportContents, "UTF-8");
      

      当然你仍然需要考虑文本编码,但至少转换是一步完成的。

      【讨论】:

      • 这个方法基本上是new ByteArrayInputStream(report.toString().getBytes("utf-8")),它涉及在内存中分配两个额外的报告副本。如果报告很大,那就不好了。看我的回答。
      【解决方案5】:

      如果您从字符串开始,您还可以执行以下操作:

      new ByteArrayInputStream(inputString.getBytes("UTF-8"))
      

      【讨论】:

      • 好的ReaderInputStream 实现需要更少的内存——应该不需要一次将所有字节存储在一个数组中。
      • 我喜欢这个解决方案,因为当您需要对接受(例如)标准输入上的输入的代码进行单元测试时,它可以工作。
      【解决方案6】:

      【讨论】:

        【解决方案7】:

        您无法避免文本编码问题,但 Apache commons-io

        请注意,这些是彼得对 koders.com 的回答中提到的库,只是指向库的链接而不是源代码。

        【讨论】:

          【解决方案8】:

          好吧,Reader 处理字符,InputStream 处理字节。编码指定您希望如何将字符表示为字节,因此您不能真正忽略这个问题。至于避免问题,我的意见是:选择一个字符集(例如“UTF-8”)并坚持下去。

          关于如何实际操作,正如已经指出的那样,“这些类的明显名称是 ReaderInputStreamWriterOutputStream”令人惊讶的是,“这些不包含在 Java 库中”,即使包含了“相反”类 InputStreamReaderOutputStreamWriter

          因此,很多人提出了自己的实现,包括 Apache Commons IO。根据许可问题,您可能可以在项目中包含 commons-io 库,甚至可以复制部分源代码(可在here 下载)。

          如您所见,两个类的文档都声明“JRE 支持的所有字符集编码都得到了正确处理”。

          注意对此处其他答案之一的评论提到this bug。但这会影响 Apache Ant ReaderInputStream 类 (here), Apache Commons IO ReaderInputStream 类。

          【讨论】:

            【解决方案9】:

            使用 WriterOutputStream 时的警告 - 它并不总是能正确处理将二进制数据写入文件/与常规输出流相同。我有一个问题,我花了一段时间才找到。

            如果可以,我建议使用输出流作为基础,如果需要编写字符串,请使用流周围的 OUTputStreamWriter 包装器来完成。将文本转换为字节比其他方式更可靠,这可能是 WriterOutputStream 不是标准 Java 库的一部分的原因

            【讨论】:

              【解决方案10】:

              用途:

              new CharSequenceInputStream(html, StandardCharsets.UTF_8);
              

              这种方式不需要预先转换为String,然后再转换为byte[],这会分配更多的堆内存,以防报告很大。当流被读取时,它会即时转换为字节,直接从 StringBuffer 中读取。

              它使用来自 Apache Commons IO 项目的CharSequenceInputStream

              【讨论】:

                【解决方案11】:

                仅使用 java 提供的内容来读取流中的字符串。

                InputStream s = new BufferedInputStream( new ReaderInputStream( new StringReader("a string")));
                

                【讨论】:

                • ReaderInputStream 在 Apache Commons IO 中。
                【解决方案12】:

                你可以使用Cactoos(没有静态方法,只有对象):

                你也可以反过来转换:

                【讨论】:

                  【解决方案13】:

                  这是一个简单的基于 UTF-8 编码的 WriterOutputStream 和 ReaderInputStream 的源代码。最后经过测试。

                      // https://www.woolha.com/tutorials/deno-utf-8-encoding-decoding-examples
                      public class WriterOutputStream extends OutputStream {
                          final Writer    writer;
                  
                          int             count       = 0;
                          int             codepoint   = 0;
                  
                          public WriterOutputStream(Writer writer) {
                              this.writer = writer;
                          }
                  
                          @Override
                          public void write(int b) throws IOException {
                              b &= 0xFF;
                              switch (b >> 4) {
                              case 0b0000:
                              case 0b0001:
                              case 0b0010:
                              case 0b0011:
                              case 0b0100:
                              case 0b0101:
                              case 0b0110:
                              case 0b0111:
                                  count = 1;
                                  codepoint = b;
                                  break;
                  
                              case 0b1000:
                              case 0b1001:
                              case 0b1010:
                              case 0b1011:
                                  codepoint <<= 6;
                                  codepoint |= b & 0b0011_1111;
                                  break;
                  
                              case 0b1100:
                              case 0b1101:
                                  count = 2;
                                  codepoint = b & 0b0001_1111;
                                  break;
                  
                              case 0b1110:
                                  count = 3;
                                  codepoint = b & 0b0000_1111;
                                  break;
                  
                              case 0b1111:
                                  count = 4;
                                  codepoint = b & 0b0000_0111;
                                  break;
                              }
                              if (--count == 0) {
                                  writer.write(codepoint);
                              }
                          }
                      }
                  
                      public class ReaderInputStream extends InputStream {
                          final Reader    reader;
                          int             count   = 0;
                          int             codepoint;
                  
                          public ReaderInputStream(Reader reader) {
                              this.reader = reader;
                          }
                  
                          @Override
                          public int read() throws IOException {
                              if (count-- > 0) {
                                  int r = codepoint >> (count * 6);
                                  r &= 0b0011_1111;
                                  r |= 0b1000_0000;
                                  return r;
                              }
                  
                              codepoint = reader.read();
                              if (codepoint < 0)
                                  return -1;
                              if (codepoint > 0xFFFF)
                                  return 0;
                  
                              if (codepoint < 0x80)
                                  return codepoint;
                  
                              if (codepoint < 0x800) {
                                  count = 1;
                                  int v = (codepoint >> 6) | 0b1100_0000;
                                  return v;
                              }
                              count = 2;
                              int v = (codepoint >> 12) | 0b1110_0000;
                              return v;
                          }
                      }
                  

                  以及验证 65536 个字符中的每一个是否正确编码和解码的测试用例,以及验证它是否与 Java 编码匹配。代理验证(2 个字符编码)被忽略,因为这是在 Java 中处理的。

                      @Test
                      public void testAll() throws IOException {
                          for (char i = 0; i < 0xFFFF; i++) {
                              CharArrayReader car = new CharArrayReader(new char[] { i });
                              ReaderInputStream rtoi = new ReaderInputStream(car);
                              byte[] data = IO.read(rtoi);
                  
                              CharArrayWriter caw = new CharArrayWriter();
                              try (WriterOutputStream wtoo = new WriterOutputStream(caw)) {
                                  wtoo.write(data);
                                  char[] translated = caw.toCharArray();
                                  assertThat(translated.length).isEqualTo(1);
                                  assertThat((int) translated[0]).isEqualTo(i);
                  
                                  if (!Character.isSurrogate((char) i)) {
                                      try (InputStream stream = new ByteArrayInputStream(data)) {
                                          caw = new CharArrayWriter();
                                          IO.copy(data, caw);
                                          translated = caw.toCharArray();
                                          assertThat(translated.length).isEqualTo(1);
                                          assertThat((int) translated[0]).isEqualTo(i);
                                      }
                                  }
                              }
                          }
                      }
                  
                  

                  【讨论】:

                    猜你喜欢
                    • 2020-09-28
                    • 2018-07-19
                    • 1970-01-01
                    • 2018-12-12
                    • 2020-08-13
                    • 2021-04-08
                    • 2022-01-02
                    • 2015-01-13
                    • 2020-11-27
                    相关资源
                    最近更新 更多