代码之家  ›  专栏  ›  技术社区  ›  Wayne Bloss

将可能以空结尾的ASCII字节[]转换为字符串的最快方法?

  •  17
  • Wayne Bloss  · 技术社区  · 17 年前

    我需要将一个(可能)以空结尾的ASCII字节数组转换为C中的字符串,我发现最快的方法是使用下面所示的unsafeasciibytestostring方法。此方法使用string.string(sbyte*)构造函数,其备注中包含警告:

    “假定value参数指向一个数组,该数组表示使用默认的ansi代码页编码的字符串(即encoding.default指定的编码方法)。

    注: *由于默认的ANSI代码页依赖于系统,因此此构造函数从相同的带符号字节数组创建的字符串在不同的系统上可能有所不同。*

    *如果指定的数组不是以空结尾的,则此构造函数的行为取决于系统。例如,这种情况可能导致访问冲突。*

    现在,我肯定字符串的编码方式永远不会改变…但是我的应用程序运行的系统上的默认代码页可能会改变。所以,有什么理由我不应该因为使用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();
            }
        }
    }
    
    8 回复  |  直到 7 年前
        1
  •  11
  •   Jon Skeet    7 年前

    有什么理由不使用 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进行适当的编码)?

        2
  •  9
  •   user3042599    11 年前

    OneLiner(假设缓冲区实际包含一个格式良好的以空结尾的字符串):

    String MyString = Encoding.ASCII.GetString(MyByteBuffer).TrimEnd((Char)0);
    
        3
  •  7
  •   Vladimir Poslavskiy    13 年前
    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)); 
        }
    
        }
    }
    
        4
  •  2
  •   Marijn    13 年前
    s = s.Substring(0, s.IndexOf((char) 0));
    
        5
  •  1
  •   Jeffrey L Whitledge    17 年前

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

    这也可以考虑字符串是否实际上是以空结尾的,但是一旦您这样做了,当然,速度会增加,我的消失。

        6
  •  1
  •   Pat    15 年前

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

    string s = myEncoding.GetString(bytes.TakeWhile(b => !b.Equals(0)).ToArray());
    
        7
  •  0
  •   Harald Coppoolse    12 年前

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

    从字符串到字节[]:

    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);
    
        8
  •  -1
  •   Adam Pierce    15 年前

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

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