Самый быстрый способ преобразовать ascii byte [] с нулевым символом в конце в строку? - PullRequest
18 голосов
/ 27 сентября 2008

Мне нужно преобразовать (возможно) нулевой завершенный массив байтов 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 ]

11 голосов
/ 27 ноября 2013

Oneliner (при условии, что буфер действительно содержит ОДНУ хорошо отформатированную строку с нулевым окончанием):

String MyString = Encoding.ASCII.GetString(MyByteBuffer).TrimEnd((Char)0);
11 голосов
/ 27 сентября 2008

Есть ли причина не использовать конструктор 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 для соответствующей кодировки)?

7 голосов
/ 24 ноября 2011
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)); 
    }

    }
}
2 голосов
/ 25 сентября 2012
s = s.Substring(0, s.IndexOf((char) 0));
2 голосов
/ 02 декабря 2009

Я не уверен в скорости, но я нашел, что проще всего использовать LINQ для удаления нулей перед кодированием:

string s = myEncoding.GetString(bytes.TakeWhile(b => !b.Equals(0)).ToArray());
1 голос
/ 27 сентября 2008

Можно рассмотреть одну возможность: проверить, что кодовая страница по умолчанию является приемлемой, и использовать эту информацию для выбора механизма преобразования во время выполнения.

Это также может учитывать, является ли строка на самом деле нулевым символом в конце, но, как только вы это сделаете, скорость, конечно, увеличится.

0 голосов
/ 11 июля 2013

Простой / безопасный / быстрый способ преобразования объектов byte [] в строки, содержащие их эквивалент ASCII, и наоборот с использованием класса .NET System.Text.Encoding. Класс имеет статическую функцию, которая возвращает кодировщик 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);
0 голосов
/ 18 марта 2010

Это немного уродливо, но вам не нужно использовать небезопасный код:

string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
   result += (char)data[i];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...