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





Есть ли причина не использовать конструктор 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 для соответствующей кодировки)?
Спасибо за ответ. Я не использовал String (sbyte *, int, int), потому что он не останавливается на первом найденном null, вместо этого он преобразует каждый null в пробел, как и Encoding.ASCII.GetString ().
О, также это не узкое место или что-то в этом роде. Я просто ботаник, которому на выходных нечего делать :)
Этот код привел к ошибке: «Невозможно получить адрес, получить размер или объявить указатель на управляемый тип 'byte []' (CS0208)». Чтобы исправить это, я удалил & из &buffer
Это не означает, что он завершается после нулевого символа. Результирующая строка имеет длину всего буфера и содержит символ \0 и дополнительные байты.
@Arek: Я предполагал, что OP будет делать это. Отредактирую, чтобы уточнить.
@Arek: На самом деле, это еще не все ... сейчас ищу.
while (offset < buffer.Length..., должен быть offset? или end.
Одна возможность для рассмотрения: проверьте, что кодовая страница по умолчанию является приемлемой, и используйте эту информацию для выбора механизма преобразования во время выполнения.
Это также может учитывать, действительно ли строка заканчивается нулем, но как только вы это сделаете, скорость, конечно же, исчезнет.
Я не уверен в скорости, но мне показалось, что проще всего использовать LINQ для удаления нулей перед кодированием:
string s = myEncoding.GetString(bytes.TakeWhile(b => !b.Equals(0)).ToArray());
Лучший ответ! Чтобы завершить ответ, не забудьте "using System.Linq;" и без myEncoding: "String s = Encoding.UTF8.GetString (rbuf.TakeWhile (b =>! b.Equals (0)). ToArray ());" где rbuf - это Byte [].
Это немного некрасиво, но вам не нужно использовать небезопасный код:
string result = "";
for (int i = 0; i < data.Length && data[i] != 0; i++)
result += (char)data[i];
Это происходит очень медленно, поскольку создается новый экземпляр строки для каждого символа. По совпадению, я делал этот точно такой же код раньше, и это оказалось моим узким местом (а строки были не более 255 символов в длину!) Это определенно нет, что OP хочет с точки зрения скорости.
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));
}
}
}
Спасибо, именно то, что мне нужно. Я подозреваю, что для многих устаревших приложений, таких как мое, кодовая страница будет 1252, и это будет именно то, что им нужно.
что произойдет, если нет нулевого завершения? Когда остановится Enc.GetString?
@Rick его остановка в конце массива "data".
s = s.Substring(0, s.IndexOf((char) 0));
Простой / безопасный / быстрый способ преобразовать объекты 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);
Это не обрабатывает нулевое завершение.
частный статический символ [] string2chars (строка S) {S + = '\ 0'; // Добавляем нулевой терминатор для строк C. byte [] bytes = System.Text.Encoding.UTF8.GetBytes (S); // Поскольку мы конвертируем в байты, '\ 0' имеет решающее значение, иначе он будет потерян char [] chars = System.Text.Encoding.UTF8.GetChars (bytes); // Можно использовать ASCII вместо return chars; }
Джефф - приведенный выше код устраняет проблему нулевого завершения
Oneliner (при условии, что буфер действительно содержит ОДНУ хорошо отформатированную строку с завершающим нулем):
String MyString = Encoding.ASCII.GetString(MyByteBuffer).TrimEnd((Char)0);
Это работает, только если буфер содержит только одну строку, начиная с индекса 0 массива.
Для полноты картины вы также можете использовать для этого встроенные методы платформы .NET:
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
return Marshal.PtrToStringAnsi(handle.AddrOfPinnedObject());
}
finally
{
handle.Free();
}
Преимущества:
Marshal.PtrToStringUni.
Упс, только что кое-что понял ... у меня нет возможности указать максимальную длину при использовании String.String (sbyte *), что в основном означает смерть от использования конструктора для чтения из кольцевого буфера, поскольку он может сохранять чтение сверх максимальной длины в следующий сегмент!