有没有任何实用的类/方法可以将大字符串转换为输入流?

6
我正在寻找一些实用的类/方法,用于将一个大的 String 转换成 InputStream
如果 String 很小,我可以这样做:
InputStream is = new ByteArrayInputStream(str.getBytes(<charset>));

但是,当这个String很大(1MB,10MB或更多)时,在现场分配一个1到2倍(或更多?)于我的String的字节数组。(而且由于在所有编码完成之前不知道准确需要分配多少字节,我认为必须在最终字节数组分配之前分配其他数组/缓冲区。)
我有性能要求,想要优化此操作。
理想情况下,我认为,我正在寻找的类/方法将一次仅对一个小块字符进行编码,随着InputStream被消耗,而没有大量的内存分配。
查看apache commons IOUtils.toInputStream(..)的源代码,我发现它也一次将String转换为一个大的字节数组。
而StringBufferInputStream已被弃用,无法正常工作。
是否有来自任何地方的util类/方法?还是我只需写几行代码即可?
这个的功能需求是,在其他地方,我使用了一个util方法,需要一个InputStream并从这个InputStream流出字节。
我没有看到其他人寻找这样的东西。我是不是错了?
我已经开始编写一个自定义类,但如果有更好/适当/正确的解决方案/纠正我的需求,我就会停止。

2
等一下...你知道吗?String在内部是一个char数组,而char有两个字节长。更重要的是,你甚至没有考虑编码问题... - fge
2
使用ReaderInputStreamStringReader上进行操作如何?另外,参见https://dev59.com/6HRA5IYBdhLWcg3wwwzD。 - shmosel
好的,我希望能找到一些让我指定我想要的编码/字符集的东西。 - Fai Ng
1
@LouisWasserman 我现在没有时间去研究它。请随意使用它。 - shmosel
这个问题的答案应该放在https://dev59.com/6HRA5IYBdhLWcg3wwwzD,我认为那里还没有正确的答案。 - Fai Ng
显示剩余4条评论
4个回答

4
Java内置库默认假定你只需要将字符转换为输出的字节,而不是输入的字节。然而,Apache Commons IO库有一个ReaderInputStream类,它可以包装一个StringReader来得到你想要的结果。

完全地,正是我想要的。 - Fai Ng

1

对我来说有一个根本性的问题。为什么你首先要在内存中拥有如此大的String...

无论如何,你可以尝试这个:

public static InputStream largeStringToBytes(final String tooLarge,
    final Charset charset)
{
    final CharsetEncoder encoder = charset.newEncoder()
        .onUnmappableCharacter(CodingErrorAction.REPORT);
    final ByteBuffer buf = charset.encode(CharBuffer.wrap(tooLarge));
    return new ByteArrayInputStream(buf.array());
}

这有什么比string.getBytes(charset)更好的呢? - Louis Wasserman
@LouisWasserman 因为它可以检测格式不正确的输入... String.getBytes() 不能。 - fge
这将复制输入字符串,就像getBytes一样。 - Petter Nordlander
@Petter 是的,但它可以检测错误!你可以使用解码器仅读取部分内容(我在这个项目中已经这样做了,但是用于反向操作),但是这里的根本问题是为什么会有这么大的字符串存在于内存中... - fge

0

实现自己的字符串支持的输入流:

class StringifiedInputStream extends InputStream {

    private int idx=0;
    private final String str;
    private final int len;

    StringifiedInputStream(String str) {
        this.str = str;
        this.len = str.length();
    }

    @Override
    public int read() throws IOException {
        if(idx>=len)
            return -1;

        return (byte) str.charAt(idx++);
    }
}

这个方法虽然慢,但是它可以在不复制字节数组的情况下流式传输字节。如果速度是一个问题,请将3个参数的方法添加到此实现中。


0

如果您将大字符串作为参数传递,则内存已经分配。这么大的字符串甚至无法推送到堆栈上(大多数情况下,最大堆栈大小为1MB),因此它会在堆上分配,只是为了将其作为参数传递。我唯一能想到的避免方法是在磁盘上创建一个树,在遍历树时每次流回一个字符。如果您有多个大字符串,可以在Trie或DAWG中对它们进行索引并遍历该结构。这将消除字符串之间的许多重复字符。但是,我需要更多了解字符串表示的信息才能提供进一步的帮助。


字符串已经被分配了。只是不想再分配另一个大的字节数组作为中间步骤。 - Fai Ng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接