如何将Reader转换为InputStream,将Writer转换为OutputStream?

时间:2008-09-15 11:51:48

标签: java stream

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

13 个答案:

答案 0 :(得分:93)

如果您开始使用String,您还可以执行以下操作:

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

答案 1 :(得分:42)

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

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

答案 2 :(得分:38)

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

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

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

正如您所看到的,两个类的文档都指出“JRE支持的所有字符集编码都能正确处理”。

N.B。对其中一个答案的评论在这里提到this bug。但这会影响Apache Ant ReaderInputStream类(here),而不是 Apache Commons IO ReaderInputStream类。

答案 3 :(得分:19)

另请注意,如果您开始使用String,则可以跳过创建StringReader并使用Commons IO中的org.apache.commons.io.IOUtils一步创建一个InputStream,如下所示:< / p>

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

当然,您仍然需要考虑文本编码,但至少转换只需一步即可完成。

答案 4 :(得分:8)

使用:

new CharSequenceInputStream(html, StandardCharsets.UTF_8);

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

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

答案 5 :(得分:7)

答案 6 :(得分:5)

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

我不确定它是否会解决所有文本编码问题,这些都是噩梦。

There is an RFE,但它已关闭,无法修复。

答案 7 :(得分:5)

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

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

答案 8 :(得分:4)

您是否尝试将Reader的内容写入OutputStream?如果是这样,您可以更轻松地将OutputStream包裹在OutputStreamWriter中,并将charReader写入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

{{1}}

答案 9 :(得分:1)

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

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

答案 10 :(得分:1)

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

您也可以转换其他方式:

答案 11 :(得分:0)

这是一个简单的基于 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);
                    }
                }
            }
        }
    }

答案 12 :(得分:-1)

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

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