23

我需要将(可能)以空结尾的 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();
        }
    }
}
4

9 回答 9

16

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

String MyString = Encoding.ASCII.GetString(MyByteBuffer).TrimEnd((Char)0);
于 2013-11-27T16:17:52.303 回答
15

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

于 2008-09-27T18:23:43.047 回答
7

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

string s = myEncoding.GetString(bytes.TakeWhile(b => !b.Equals(0)).ToArray());
于 2009-12-02T19:18:24.103 回答
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)); 
    }

    }
}
于 2011-11-23T21:00:23.520 回答
4
s = s.Substring(0, s.IndexOf((char) 0));
于 2012-09-25T14:09:37.353 回答
1

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

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

于 2008-09-27T18:25:42.637 回答
1

为了完整起见,您还可以使用 .NET 框架的内置方法来执行此操作:

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

好处:

  • 它不需要不安全的代码(即,您也可以将此方法用于 VB.NET)和
  • 如果您改用它,它也适用于“宽”(UTF-16)字符串Marshal.PtrToStringUni
于 2019-09-17T13:55:48.410 回答
0

使用 .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);
于 2013-07-11T08:27:26.940 回答
-2

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

string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
   result += (char)data[i];
于 2010-03-17T22:03:23.350 回答