Самый быстрый способ конвертировать байт ascii с номером с нулевым завершением [] в строку?
Мне нужно преобразовать (возможно) нуль-завершенный массив ascii-байтов в строку в С#, и самый быстрый способ, который я нашел, это использовать мой метод UnsafeAsciiBytesToString, показанный ниже. Этот метод использует конструктор String.String(sbyte *), который содержит предупреждение в нем:
"Предполагается, что параметр значения указывает на массив, представляющий строку, закодированную с использованием кодовой страницы 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();
}
}
}
Ответы
Ответ 1
Любая причина не использовать конструктор 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 для соответствующей кодировки)?
Ответ 2
Oneliner (предполагая, что в буфере фактически содержится ОДНА хорошо отформатированная строка с нулевым завершением):
String MyString = Encoding.ASCII.GetString(MyByteBuffer).TrimEnd((Char)0);
Ответ 3
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));
}
}
}
Ответ 4
s = s.Substring(0, s.IndexOf((char) 0));
Ответ 5
Я не уверен в скорости, но мне было проще использовать LINQ для удаления нулей перед кодировкой:
string s = myEncoding.GetString(bytes.TakeWhile(b => !b.Equals(0)).ToArray());
Ответ 6
Одна возможность рассмотреть: убедитесь, что кодовая страница по умолчанию приемлема и использует эту информацию для выбора механизма преобразования во время выполнения.
Это также может принимать во внимание, действительно ли строка на самом деле завершена с нулевой отметкой, но, как только вы это сделали, конечно, скорость меня исчезает.
Ответ 7
Легкий/безопасный/быстрый способ конвертировать байтовые [] объекты в строки, содержащие их эквивалент ASCII, и наоборот, используя .NET class System.Text.Encoding. Класс имеет статическую функцию, которая возвращает ASCII-кодер:
От String to byte []:
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);
Ответ 8
Для полноты картины вы также можете использовать встроенные методы .NET Framework для этого:
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
return Marshal.PtrToStringAnsi(handle.AddrOfPinnedObject());
}
finally
{
handle.Free();
}
Преимущества:
- Он не требует небезопасного кода (то есть вы также можете использовать этот метод для VB.NET) и
- он также работает для "широких" (UTF-16) строк, если вместо этого вы используете
Marshal.PtrToStringUni
.
Ответ 9
Это немного уродливо, но вам не нужно использовать небезопасный код:
string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
result += (char)data[i];