Le moyen le plus rapide de convertir un octet ascii [] éventuellement terminé par un caractère null en chaîne?

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

  •  02-07-2019
  •  | 
  •  

Question

Je dois convertir un tableau d'octets ascii (null) terminé par une chaîne en C #, et le moyen le plus rapide de le faire consiste à utiliser ma méthode UnsafeAsciiBytesToString présentée ci-dessous. Cette méthode utilise le constructeur String.String (sbyte *) qui contient un avertissement dans ses remarques:

" Le paramètre value est supposé pointer sur un tableau représentant une chaîne codée à l'aide de la page de code ANSI par défaut (c'est-à-dire la méthode de codage spécifiée par Encoding.Default).

Remarque: * La page de codes ANSI par défaut étant dépendante du système, la chaîne créée par ce constructeur à partir de tableaux d'octets signés identiques peut différer d'un système à l'autre. * ...

* Si le tableau spécifié n'est pas terminé par zéro, le comportement de ce constructeur dépend du système. Par exemple, une telle situation peut entraîner une violation d'accès. * "

Maintenant, je suis convaincu que la façon dont la chaîne est codée ne changera jamais ... mais la page de codes par défaut sur le système sur lequel mon application est exécutée peut changer. Donc, y a-t-il une raison pour laquelle je ne devrais pas courir à crier d'utiliser String.String (sbyte *) à cette fin?

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();
        }
    }
}
Était-ce utile?

La solution

Une raison de ne pas utiliser le constructeur String (sbyte *, int, int) ? Si vous avez défini la partie du tampon dont vous avez besoin, le reste devrait être simple:

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

Si vous devez d'abord regarder:

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

S'il s'agit vraiment d'une chaîne ASCII (c'est-à-dire que tous les octets sont inférieurs à 128), le problème de la page de code ne devrait pas être un problème à moins que vous n'ayez une particulièrement page de code étrange qui ne soit pas basé sur ASCII.

Par intérêt, avez-vous réellement profilé votre application pour vous assurer que c'est vraiment le goulot d'étranglement? Avez-vous vraiment besoin de la conversion la plus rapide absolue, au lieu d’une conversion plus lisible (par exemple, en utilisant Encoding.GetString pour le codage approprié)?

Autres conseils

Oneliner (en supposant que la mémoire tampon contienne réellement UNE chaîne bien terminée par un caractère NULL):

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

    }
}

Je ne suis pas sûr de la vitesse, mais j'ai trouvé plus simple d'utiliser LINQ pour supprimer les valeurs NULL avant l'encodage:

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

Une possibilité à envisager: vérifier que la page de code par défaut est acceptable et utiliser ces informations pour sélectionner le mécanisme de conversion au moment de l'exécution.

Ceci pourrait également prendre en compte le fait que la chaîne est effectivement terminée par un caractère null, mais une fois que vous avez fait cela, bien sûr, la vitesse gagne mon effacement.

Un moyen facile / sûr / rapide de convertir des objets byte [] en chaînes contenant leur équivalent ASCII et inversement à l'aide de la classe .NET System.Text.Encoding. La classe a une fonction statique qui retourne un encodeur ASCII:

De chaîne en octet []:

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

De octet [] à chaîne:

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

C’est un peu moche mais vous n’aurez pas à utiliser de code non sécurisé:

string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
   result += (char)data[i];
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top