¿La forma más rápida de convertir un byte ASCII finalizado en nulo [] en una cadena?

StackOverflow https://stackoverflow.com/questions/144176

  •  02-07-2019
  •  | 
  •  

Pregunta

Necesito convertir una matriz (posiblemente) terminada en nulo de bytes ASCII a una cadena en C # y la forma más rápida que he encontrado es usando mi método UnsafeAsciiBytesToString que se muestra a continuación. Este método utiliza el constructor String.String (sbyte *) que contiene una advertencia en sus comentarios:

" Se supone que el parámetro de valor apunta a una matriz que representa una cadena codificada utilizando la página de códigos ANSI predeterminada (es decir, el método de codificación especificado por Encoding.Default).

Nota: * Debido a que la página de códigos ANSI predeterminada depende del sistema, la cadena creada por este constructor a partir de matrices de bytes con signo idéntico puede diferir en diferentes sistemas. * ...

* Si la matriz especificada no está terminada en nulo, el comportamiento de este constructor depende del sistema. Por ejemplo, tal situación podría causar una violación de acceso. * "

Ahora, estoy seguro de que la forma en que se codifica la cadena nunca cambiará ... pero la página de códigos predeterminada en el sistema en el que se está ejecutando mi aplicación podría cambiar. Entonces, ¿hay alguna razón por la que no deba correr gritando usando String.String (sbyte *) para este propósito?

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();
        }
    }
}
¿Fue útil?

Solución

¿Alguna razón para no usar el constructor String (sbyte *, int, int) ? Si has averiguado qué parte del búfer necesitas, el resto debería ser simple:

public static string UnsafeAsciiBytesToString(byte[] buffer, int offset, int length)
{
    unsafe
    {
       fixed (byte* pAscii = buffer)
       { 
           return new String((sbyte*)pAscii, offset, length);
       }
    }
}

Si necesitas mirar primero:

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

Si esto realmente es una cadena ASCII (es decir, todos los bytes son menos de 128), entonces el problema de la página de códigos no debería ser un problema a menos que tengas una particularmente página de códigos predeterminada que no sea basado en ASCII.

Interesado, ¿realmente ha perfilado su aplicación para asegurarse de que este es realmente el cuello de botella? ¿Definitivamente necesitas la conversión más rápida absoluta, en lugar de una que sea más legible (por ejemplo, utilizando Encoding.GetString para la codificación adecuada)?

Otros consejos

Oneliner (asumiendo que el búfer en realidad contiene UNA cadena terminada en nulo bien formateada):

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

    }
}

No estoy seguro de la velocidad, pero me resultó más fácil usar LINQ para eliminar los nulos antes de codificar:

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

Una posibilidad a considerar: verifique que la página de códigos predeterminada sea aceptable y use esa información para seleccionar el mecanismo de conversión en tiempo de ejecución.

Esto también podría tener en cuenta si la cadena está terminada en nulo, pero una vez que lo hayas hecho, la velocidad se desvanecerá.

Una forma fácil / segura / rápida de convertir objetos byte [] en cadenas que contienen su equivalente ASCII y viceversa mediante la clase .NET System.Text.Encoding. La clase tiene una función estática que devuelve un codificador ASCII:

De cadena a byte []:

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

De byte [] a cadena:

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

Esto es un poco feo, pero no tienes que usar código inseguro:

string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
   result += (char)data[i];
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top