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

时间:2008-09-27 18:13:38

标签: c# .net string ascii

我需要将一个(可能的)空终止的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();
        }
    }
}

9 个答案:

答案 0 :(得分: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进行适当的编码)吗?

答案 1 :(得分:10)

Oneliner(假设缓冲区实际上包含一个格式良好的空终止字符串):

String MyString = Encoding.ASCII.GetString(MyByteBuffer).TrimEnd((Char)0);

答案 2 :(得分:7)

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)); 
    }

    }
}

答案 3 :(得分:2)

我不确定速度,但我发现在编码之前使用LINQ删除空值最简单:

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

答案 4 :(得分:2)

s = s.Substring(0, s.IndexOf((char) 0));

答案 5 :(得分:1)

考虑的一种可能性:检查默认代码页是否可接受,并使用该信息在运行时选择转换机制。

这也可以考虑字符串是否实际上是空终止的,但是一旦你完成了这一点,当然,速度会让我消失。

答案 6 :(得分:0)

使用.NET类System.Text.Encoding将byte []对象转换为包含ASCII等效字符串的字符串的简单/安全/快速方法,反之亦然。该类有一个静态函数,它返回一个ASCII编码器:

从String到byte []:

string s = "Hello World!"
byte[] b = System.Text.Encoding.ASCII.GetBytes(s);

从byte []到string:

byte[] byteArray = new byte[] {0x41, 0x42, 0x09, 0x00, 0x255};
string s = System.Text.Encoding.ASCII.GetString(byteArray);

答案 7 :(得分:0)

仅出于完整性考虑,您还可以使用.NET框架的内置方法来做到这一点:

var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
    return Marshal.PtrToStringAnsi(handle.AddrOfPinnedObject());
}
finally
{
    handle.Free();
}

优势:

  • 它不需要不安全的代码(即,您也可以将这种方法用于VB.NET)和
  • 如果您改用Marshal.PtrToStringUni,它也适用于“宽”(UTF-16)字符串。

答案 8 :(得分:-1)

这有点难看,但您不必使用不安全的代码:

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