将可能为空的ascii byte []转换为字符串的最快方法?

时间:2020-03-06 14:49:43  来源:igfitidea点击:

我需要使用以下所示的我的UnsafeAsciiBytesToString方法,以最快的方式将Canci字节中一个(可能)为null的终止字节数组转换为Cand中的字符串。此方法使用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();
        }
    }
}

解决方案

有什么理由不使用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删除null最为简单:

string s = myEncoding.GetString(bytes.TakeWhile(b => !b.Equals(0)).ToArray());

这有点丑陋,但我们不必使用不安全的代码:

string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
   result += (char)data[i];