Il modo più veloce per convertire un byte ASCII [] eventualmente terminato da null in una stringa?

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

  •  02-07-2019
  •  | 
  •  

Domanda

Ho bisogno di convertire un array (eventualmente) null di byte ascii in una stringa in C # e il modo più veloce che ho trovato per farlo è usando il mio metodo UnsafeAsciiBytesToString mostrato di seguito. Questo metodo utilizza il costruttore String.String (sbyte *) che contiene un avviso nelle sue osservazioni:

" Si presume che il parametro value punti a un array che rappresenta una stringa codificata utilizzando la tabella codici ANSI predefinita (ovvero il metodo di codifica specificato da Encoding.Default).

Nota: * Poiché la tabella codici ANSI predefinita dipende dal sistema, la stringa creata da questo costruttore da matrici di byte con segno identico può differire su sistemi diversi. * ...

* Se l'array specificato non ha terminazione nulla, il comportamento di questo costruttore dipende dal sistema. Ad esempio, una situazione del genere potrebbe causare una violazione di accesso. * & Quot;

Ora, sono sicuro che il modo in cui la stringa è codificata non cambierà mai ... ma la tabella codici predefinita sul sistema su cui è in esecuzione la mia app potrebbe cambiare. Quindi, c'è qualche motivo per cui non dovrei correre urlando dall'usare String.String (sbyte *) per questo scopo?

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();
        }
    }
}
È stato utile?

Soluzione

Qualche motivo per non usare il costruttore String (sbyte *, int, int) ? Se hai capito quale parte del buffer ti serve, il resto dovrebbe essere semplice:

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

Se devi prima guardare:

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

Se questa è veramente una stringa ASCII (ovvero tutti i byte sono inferiori a 128), il problema con la codepage non dovrebbe essere un problema a meno che tu non abbia una strana codepage predefinita particolarmente che non lo è basato su ASCII.

Per interesse, hai effettivamente profilato la tua domanda per assicurarti che questo sia davvero il collo di bottiglia? Hai sicuramente bisogno della conversione più veloce in assoluto, anziché di una più leggibile (ad es. Utilizzando Encoding.GetString per la codifica appropriata)?

Altri suggerimenti

Oneliner (supponendo che il buffer contenga effettivamente UNA stringa con terminazione null ben formattata):

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

    }
}

Non sono sicuro della velocità, ma ho trovato più facile usare LINQ per rimuovere i null prima della codifica:

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

Una possibilità da considerare: verificare che la tabella codici predefinita sia accettabile e utilizzare tali informazioni per selezionare il meccanismo di conversione in fase di esecuzione.

Ciò potrebbe anche tenere conto del fatto che la stringa sia effettivamente terminata con null, ma una volta che lo hai fatto, ovviamente, la velocità guadagna il mio svanire.

Un modo semplice / sicuro / veloce per convertire oggetti byte [] in stringhe contenenti il ??loro equivalente ASCII e viceversa usando la classe .NET System.Text.Encoding. La classe ha una funzione statica che restituisce un codificatore ASCII:

Da stringa a byte []:

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

Da byte [] a stringa:

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

Questo è un po 'brutto ma non è necessario utilizzare un codice non sicuro:

string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
   result += (char)data[i];
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top