编码API可以解码流/非连续字节吗?

时间:2019-03-03 15:34:39

标签: c# character-encoding .net-standard

通常,我们可以使用类似的方法从string中获得byte[]

var result = Encoding.UTF8.GetString(bytes);

但是,我遇到了这个问题:我的输入是IEnumerable<byte[]> bytes(实现可以是我选择的任何结构)。不能保证字符位于byte[]之内(例如,一个2字节的UTF8字符的第一个字节可以为bytes [1] [length-1],第二个字节可以为bytes [2] [0] ])。

是否仍然可以在不将所有数组合并/复制在一起的情况下对其进行解码? UTF8是主要重点,但最好是支持其他编码。如果没有其他解决方案,我认为可以实现自己的UTF8阅读。

我计划使用MemoryStream进行流式传输,但是编码无法在Stream上运行,而只能在byte[]上运行。如果合并在一起,则可能的结果数组可能会非常大(List<byte[]>中已高达4GB)。

我正在使用.NET Standard 2.0。我希望我可以使用2.1(因为它尚未发布)并使用Span<byte[]>对我的情况来说是完美的!

3 个答案:

答案 0 :(得分:2)

Encoding类不能直接处理它,但是从Decoder返回的Encoding.GetDecoder()可以(实际上,这就是它存在的全部原因)。 StreamReader在内部使用Decoder

虽然有点麻烦,但是它需要填充char[],而不是返回stringEncoding.GetString()StreamReader通常用于处理填充char[])。

使用MemoryStream的问题在于,您要将所有字节从一个数组复制到另一个数组,没有任何收益。如果所有缓冲区的长度都相同,则可以执行以下操作:

var decoder = Encoding.UTF8.GetDecoder();
// +1 in case it includes a work-in-progress char from the previous buffer
char[] chars = decoder.GetMaxCharCount(bufferSize) + 1;
foreach (var byteSegment in bytes)
{
    int numChars = decoder.GetChars(byteSegment, 0, byteSegment.Length, chars, 0);
    Debug.WriteLine(new string(chars, 0, numChars));
}

如果缓冲区的长度不同:

var decoder = Encoding.UTF8.GetDecoder();
char[] chars = Array.Empty<char>();
foreach (var byteSegment in bytes)
{
    // +1 in case it includes a work-in-progress char from the previous buffer
    int charsMinSize = decoder.GetMaxCharCount(bufferSize) + 1;
    if (chars.Length < charsMinSize)
        chars = new char[charsMinSize];
    int numChars = decoder.GetChars(byteSegment, 0, byteSegment.Length, chars, 0);
    Debug.WriteLine(new string(chars, 0, numChars));
}

答案 1 :(得分:1)

  

但是编码只能在字节[]上不能在Stream上工作。

正确,但StreamReader : TextReader可以链接到流。

因此,只需创建该MemoryStream,在一端插入字节,在另一端使用ReadLine()。我必须说我从未尝试过。

答案 2 :(得分:0)

使用StreamReader基于Henk的答案的工作代码:

    using (var memoryStream = new MemoryStream())
    {
        using (var reader = new StreamReader(memoryStream))
        {
            foreach (var byteSegment in bytes)
            {
                memoryStream.Seek(0, SeekOrigin.Begin);
                await memoryStream.WriteAsync(byteSegment, 0, byteSegment.Length);
                memoryStream.Seek(0, SeekOrigin.Begin);

                Debug.WriteLine(await reader.ReadToEndAsync());
            }
        }
    }