我需要将一个(可能)以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(); } } }
Jon Skeet.. 11
有什么理由不使用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进行适当的编码)?
有什么理由不使用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进行适当的编码)?
Oneliner(假设缓冲区实际上包含一个格式良好的空终止字符串):
String MyString = Encoding.ASCII.GetString(MyByteBuffer).TrimEnd((Char)0);
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)); } } }