通常我们可以使用类似的东西从string
获得byte[]
var result = Encoding.UTF8.GetString(bytes);
但是,我遇到了这个问题:我的输入是IEnumerable<byte[]> bytes
(实现可以是我选择的任何结构)。不保证字符在byte[]
内(例如,一个2字节的UTF8字符可以在字节[1] [长度为1]中具有其第一个字节,在字节[2] [0]中具有其第二个字节)。
无论如何解码它们而不将所有阵列合并/复制在一起? UTF8是主要关注点,但如果可以支持其他编码则更好。如果没有其他解决方案,我认为实现我自己的UTF8读数就是这样。
我计划使用MemoryStream
流式传输它们,但是编码无法在Stream
上工作,只需byte[]
。如果合并在一起,潜在的结果数组可能非常大(已经在List<byte[]>
中高达4GB)。
我使用的是.NET Standard 2.0。我希望我能使用2.1(因为它尚未发布)并使用Span<byte[]>
,对我的情况来说是完美的!
Encoding
类不能直接处理,但Decoder
返回的Encoding.GetDecoder()
可以(事实上,这是它存在的全部原因)。 StreamReader
内部使用Decoder
。
虽然它有点繁琐,因为它需要填充char[]
,而不是返回string
(Encoding.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));
}
但是编码不能在Stream上工作,只有byte []。
正确,但StreamReader : TextReader
可以链接到流。
因此,只需创建MemoryStream,在一端推入字节,在另一端使用ReadLine()。我必须说我从未尝试过。
基于Henk使用StreamReader
的答案的工作代码:
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());
}
}
}