将字节数组转换为由 NUL 字符分割的字符串

问题描述 投票:0回答:1

如果这是个愚蠢的问题,我很抱歉。但我真的无法弄清楚这一点,而且我敢打赌它一定比我想象的要简单得多。

我有一个

byte[]
数组,其中包含多个Unicode字符串,每个字符明显占用2个字节,每个字符串由两个00 00字节分隔,直到双00 00标志着这一切的结束。

当我尝试使用

UnicodeEncoding.Unicode.GetString(myBuffer)
时,我确实得到了第一个字符串,但是当找到分隔符字节时,它开始到处都是垃圾。

现在我正在逐字节解析,然后连接事物,但我确信必须有更好的方法。

我想知道是否应该尝试找到分隔符字节的“位置”,然后将

GetString
方法限制为该借出?但如果是这样,你如何找到 2 个字节数组中 2 个特定字节的位置?

示例字节数组如下所示:

Hex View
 
00000000  73 00 74 00 72 00 31 00  00 00 73 00 74 00 72 00  s.t.r.1...s.t.r.
00000010  32 00 00 00 73 00 74 00  72 00 33 00 00 00 00 00  2...s.t.r.3.....
c# arrays split unicode-string
1个回答
4
投票

因此您的缓冲区是有效的小端 UTF-16 数据。那些“双 00 字节”只是 NUL 字符,或

\0

Encoding.Unicode.GetString(myBuffer)
实际上会正确解码整个缓冲区,但它会在其中嵌入 NUL 字符来分隔每个子字符串。这很好,因为
\0
就像任何角色一样。这不是C。

下面的示例代码将使用

Console.WriteLine
来表示“使用子字符串”,但请随意替换为合适的内容。

第一种方法:解码整个事情

如果解码后按

\0
分割,您可以获得所有子字符串,删除空条目以摆脱那些最终的 NUL:

var decoded = Encoding.Unicode.GetString(myBuffer);
foreach(var str in decoded.Split('\0', StringSplitOptions.RemoveEmptyEntries))
    Console.WriteLine(str);

或者,如果需要,您可以搜索第一个 NUL:

var index = decoded.IndexOf('\0');
var firstStr = decoded.Substring(0, index);

第二种方法:拆分,然后解码

如果您不想一次性完成所有操作,因为您必须一次处理大量数据,那么您可以将缓冲区映射到一系列短裤,然后找到下一个

0
,然后从那里解码:

var units = MemoryMarshal.Cast<byte, short>(myBuffer);
while (!units.IsEmpty)
{
    var index = units.IndexOf<short>(0);
    if (index == -1)
        break;

    if (index > 0)
    {
        var str = Encoding.Unicode.GetString(MemoryMarshal.AsBytes(units[..index]));
        Console.WriteLine(str);
    }
    units = units[(index + 1)..];
}

或者,转换为

char
的范围,这将允许您在范围上使用
ToString()
来获取字符串,跳过解码步骤,但这假设数据都是有效文本(最终,您的所有内容)正在做的是跳过验证)。由你决定。

第三种方法:从流中逐字符读取

但是,如果您手头有那么多数据,您可能应该从流中读取数据,使用

StreamReader
进行解码:

using var stream = new MemoryStream(myBuffer, false);
using var reader = new StreamReader(stream, Encoding.Unicode);
    
var current = new StringBuilder();
int c;
while((c = reader.Read()) != -1)
{
    if (c == 0)
    {
        if(current.Length > 0)
        {
            var str = current.ToString();
            Console.WriteLine(str);
            current.Clear();
        }
    }
    else
    {
        current.Append((char)c);
    }
}

第四种方法:批量从流中读取

对上面代码的优化是使用一个字符块调用

Read
,然后自己将数据拼凑回来:

using var stream = new MemoryStream(myBuffer, false);
using var reader = new StreamReader(stream, Encoding.Unicode);
    
Span<char> batch = stackalloc char[4096];
    
var current = new StringBuilder();
int read;
while ((read = reader.Read(batch)) > 0)
{
    var left = batch[..read];
    while (!left.IsEmpty)
    {
        var index = left.IndexOf('\0');
        if (index == -1)
        {
            current.Append(left);
            break;
        }
        else
        {
            current.Append(left[..index]);
            
            // we have a string, collect it
            var str = current.ToString();
            Console.WriteLine(str);

            current.Clear();
                
            left = left[(index + 1)..];
        }
    }
}
    
if(current.Length > 0)
{
    // don't forget letfovers
    var str = current.ToString();
    Console.WriteLine(str);
}

这应该会给你带来不错的结果。

现在,这两种最终方法使用

StringBuilder
来构建子字符串,但您不必这样做,您可以将这些字符发送到其他地方(也许您正在将它们写入文件)。

© www.soinside.com 2019 - 2024. All rights reserved.