我需要将(可能)以 null 结尾的 ascii 字节数组转换为 C# 中的字符串,我发现最快的方法是使用如下所示的 UnsafeAsciiBytesToString 方法。此方法使用 String.String(sbyte*) 构造函数,其注释中包含警告:
“假定 value 参数指向一个表示使用默认 ANSI 代码页(即 Encoding.Default 指定的编码方法)编码的字符串的数组。
注意: * 由于默认的 ANSI 代码页与系统相关,因此此构造函数从相同的有符号字节数组创建的字符串在不同的系统上可能会有所不同。 *...
* 如果指定的数组不是以 null 终止的,则此构造函数的行为取决于系统。例如,这种情况可能会导致访问冲突。 * ”
现在,我确信字符串的编码方式永远不会改变......但我的应用程序运行的系统上的默认代码页可能会改变。那么,有什么理由让我不应该为此目的而使用 String.String(sbyte*) 尖叫呢?
using System;
using System.Text;
namespace FastAsciiBytesToString
{
static class StringEx
{
public static string AsciiBytesToString(this byte[] buffer, int offset, int maxLength)
{
int maxIndex = offset + maxLength;
for( int i = offset; i < maxIndex; i++ )
{
/// Skip non-nulls.
if( buffer[i] != 0 ) continue;
/// First null we find, return the string.
return Encoding.ASCII.GetString(buffer, offset, i - offset);
}
/// Terminating null not found. Convert the entire section from offset to maxLength.
return Encoding.ASCII.GetString(buffer, offset, maxLength);
}
public static string UnsafeAsciiBytesToString(this byte[] buffer, int offset)
{
string result = null;
unsafe
{
fixed( byte* pAscii = &buffer[offset] )
{
result = new String((sbyte*)pAscii);
}
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
byte[] asciiBytes = new byte[]{ 0, 0, 0, (byte)'a', (byte)'b', (byte)'c', 0, 0, 0 };
string result = asciiBytes.AsciiBytesToString(3, 6);
Console.WriteLine("AsciiBytesToString Result: \"{0}\"", result);
result = asciiBytes.UnsafeAsciiBytesToString(3);
Console.WriteLine("UnsafeAsciiBytesToString Result: \"{0}\"", result);
/// Non-null terminated test.
asciiBytes = new byte[]{ 0, 0, 0, (byte)'a', (byte)'b', (byte)'c' };
result = asciiBytes.UnsafeAsciiBytesToString(3);
Console.WriteLine("UnsafeAsciiBytesToString Result: \"{0}\"", result);
Console.ReadLine();
}
}
}
Oneliner(假设缓冲区实际上包含一个格式良好的空终止字符串):
String MyString = Encoding.ASCII.GetString(MyByteBuffer).TrimEnd((Char)0);
有什么理由不使用
String(sbyte*, int, int)
构造函数?如果您已经确定了需要缓冲区的哪一部分,那么其余部分应该很简单:
public static string UnsafeAsciiBytesToString(byte[] buffer, int offset, int length)
{
unsafe
{
fixed (byte* pAscii = buffer)
{
return new String((sbyte*)pAscii, offset, length);
}
}
}
如果您需要先看:
public static string UnsafeAsciiBytesToString(byte[] buffer, int offset)
{
int end = offset;
while (end < buffer.Length && buffer[end] != 0)
{
end++;
}
unsafe
{
fixed (byte* pAscii = buffer)
{
return new String((sbyte*)pAscii, offset, end - offset);
}
}
}
如果这确实是一个 ASCII 字符串(即所有字节都小于 128),那么代码页问题不应该成为问题,除非您有一个特别是奇怪的默认代码页,它不基于 ASCII。
出于兴趣,您是否真正分析过您的应用程序以确保这确实是瓶颈?您是否确实需要绝对最快的转换,而不是更具可读性的转换(例如使用 Encoding.GetString 进行适当的编码)?
我不确定速度,但我发现在编码之前使用 LINQ 删除空值是最简单的:
string s = myEncoding.GetString(bytes.TakeWhile(b => !b.Equals(0)).ToArray());
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestProject1
{
class Class1
{
static public string cstr_to_string( byte[] data, int code_page)
{
Encoding Enc = Encoding.GetEncoding(code_page);
int inx = Array.FindIndex(data, 0, (x) => x == 0);//search for 0
if (inx >= 0)
return (Enc.GetString(data, 0, inx));
else
return (Enc.GetString(data));
}
}
}
s = s.Substring(0, s.IndexOf((char) 0));
为了完整起见,您还可以使用.NET框架的内置方法来执行此操作:
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
return Marshal.PtrToStringAnsi(handle.AddrOfPinnedObject());
}
finally
{
handle.Free();
}
优点:
Marshal.PtrToStringUni
代替,它也适用于“宽”(UTF-16) 字符串。要考虑的一种可能性:检查默认代码页是否可接受,并使用该信息在运行时选择转换机制。
这还可以考虑字符串实际上是否以空结尾,但是一旦你这样做了,当然,速度就会消失。
使用 .NET 类 System.Text.Encoding 将 byte[] 对象转换为包含其 ASCII 等效项的字符串,反之亦然。该类有一个返回 ASCII 编码器的静态函数:
从 String 到 byte[]:
string s = "Hello World!"
byte[] b = System.Text.Encoding.ASCII.GetBytes(s);
从字节[]到字符串:
byte[] byteArray = new byte[] {0x41, 0x42, 0x09, 0x00, 0x255};
string s = System.Text.Encoding.ASCII.GetString(byteArray);
这就是我最终所做的。我不想碰
Encoding
,因为我担心这可能会改变数组中字节/字符的含义。
charsToString(char[] data)
{
return new string(data, 0, Array.IndexOf(data, '\0');
}
我相信这是最短的解决方案并且速度最快。
值得注意的是,与其他一些解决方案相反(例如,创建一个新字符串然后修剪它),这会精确分配一个字符串。
这有点难看,但你不必使用不安全的代码:
string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
result += (char)data[i];