任何 util 类/方法来获取大字符串并返回 InputStream?

Posted

技术标签:

【中文标题】任何 util 类/方法来获取大字符串并返回 InputStream?【英文标题】:Any util class/method to take a large String and return an InputStream? 【发布时间】:2015-03-10 14:53:57 【问题描述】:

我正在寻找一些实用类/方法来获取一个大的String 并返回一个InputStream

如果String 很小,我可以这样做:

InputStream is = new ByteArrayInputStream(str.getBytes(<charset>));

但是当String 很大(1MB、10MB 或更多)时,会当场分配一个 1 到 2 倍(或更多?)与我的 String 一样大的字节数组。 (而且由于在所有编码完成之前您不知道要分配多少字节,我认为在分配最终字节数组之前必须分配其他数组/缓冲区)。

我有性能要求,想优化这个操作。

我认为,理想情况下,我正在寻找的类/方法会在 InputStream 被消耗时一次对一个小块的字符进行动态编码,因此不会大幅增加内存分配。

看了一下apache commonsIOUtils.toInputStream(..)的源码,看到它也一口气把String转成大字节数组了。

StringBufferInputStream 已被弃用,并且无法正常工作。

在任何地方都有这样的 util 类/方法吗?或者我可以写几行代码来做到这一点?

对此的功能需求是,在其他地方,我正在使用一个 util 方法,该方法采用 InputStream 并从该 InputStream 流出字节。

我似乎没有其他人在寻找这样的东西。我是不是在某个地方弄错了什么?

我已经开始为此编写一个自定义类,但如果有更好/正确/正确的解决方案/更正我的需要,我会停止。

【问题讨论】:

等等...您知道String 是一个内部chars 的数组,而char 是两个bytes 长吗?更重要的是,你甚至不考虑编码...... 在StringReader 之上使用ReaderInputStream 怎么样?另请参阅***.com/questions/837703/…。 对,我希望能找到一些东西,让我指定我想要的编码/字符集。 @shmosel,我会把它作为答案。 @LouisWasserman 现在没有时间研究它。随意使用它。 【参考方案1】:

Java 内置库假定您只需要在输出中从字符转换为字节,而不是输入。 Apache Commons IO 库有 ReaderInputStream,但是,它可以包装 StringReader 来获得你想要的。

【讨论】:

正是,正是我想要的【参考方案2】:

实现您自己的基于字符串的输入流:

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-arg 方法。

【讨论】:

【参考方案3】:

如果您将大字符串作为参数传递,则内存已分配。一个很大的字符串甚至不能被推入堆栈(大多数情况下最大堆栈大小为 1MB),因此在堆上分配它只是为了将其作为参数传递。我能看到避免这种情况的唯一方法是在磁盘上创建一棵树,当你在树上行走时,你一次流回一个字符。如果您有多个大字符串,也许可以在 Trie 或 DAWG 中对它们进行索引并遍历该结构。这将消除字符串之间的许多重复字符。但是,我需要进一步了解这些字符串所代表的含义。

【讨论】:

字符串已分配。只是不想分配另一个大字节数组作为中间步骤。【参考方案4】:

对我来说,有一个基本问题。为什么你的内存里有这么大的Strings...

不管怎样,你可以试试这个:

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)更好? @LouisWasserman 因为它可以检测到格式错误的输入...String.getBytes() 不会。 这会复制输入字符串,就像 getBytes 一样。 @Petter 是的,但它会检测到错误!您可以使用您的解码器仅读取部分内容(我在this project 中已经这样做了,但用于反向操作),但无论如何,这里的根本问题是为什么内存中存在如此大的字符串。 .

以上是关于任何 util 类/方法来获取大字符串并返回 InputStream?的主要内容,如果未能解决你的问题,请参考以下文章

Java Scanner 类

Java Scanner 类

Java Scanner 类

Scanner对象

Java流程控制

java Scanner和异常