EDIT: я придумал решение, вот оно для всех, кому оно может понадобиться. Он может быть обновлен в будущем, если будет обнаружена ошибка или добавлены другие улучшения. Последнее обновление 18.07.2015.
/// <summary>
/// Decodes a string from the specified bytes in the specified encoding.
/// </summary>
/// <param name="Length">Specify -1 to read until null, otherwise, specify the amount of bytes that make up the string.</param>
public static string GetString(byte[] Source, int Offset, int Length, Encoding Encoding)
{
if (Length == 0) return string.Empty;
var sb = new StringBuilder();
if (Length <= -1)
{
using (var sr = new StreamReader(new MemoryStream(Source, Offset, Source.Length - Offset), Encoding, false))
{
int ch;
while (true)
{
ch = sr.Read();
if (ch <= 0) break;
sb.Append((char)ch);
}
if (ch == -1) throw new Exception("End of stream reached; null terminator not found.");
return sb.ToString();
}
}
else return Encoding.GetString(Source, Offset, Length);
}
Я обновляю внутреннюю строку/кодировку своего приложения и столкнулся с небольшой проблемой реализации.
По сути, я хотел сделать простой метод ReadNullTerminatedString. Поначалу сделать не составило большого труда. Я использовал Encoding.IsSingleByte для определения длины одного символа, считывал байты, проверял наличие 0 и прекращал чтение/продолжал на основе результата.
Вот где это становится сложно. UTF8 имеет кодировку переменной длины. Encoding.IsSingleByte возвращает false, но это не всегда правильно, так как это переменная кодировка, а символ может быть 1 байт, поэтому моя реализация, основанная на Encoding.IsSingleByte, не будет работать для UTF8.
В тот момент я не был уверен, что этот метод можно исправить, поэтому у меня возникла другая идея. Просто используйте метод кодировки GetString для байтов, используйте максимальную длину строки для параметра count, а затем обрежьте нули из возвращаемой строки.
Это тоже имеет оговорку. Я должен рассмотреть случаи, когда мои управляемые приложения будут взаимодействовать с массивами байтов, возвращаемыми из неуправляемого кода, случаи, когда, конечно, будет нулевой терминатор, но возможность наличия дополнительных ненужных символов после него. Например: "blah\0\0\oldstring"
ReadNullTerminatedString был бы идеальным решением в этом случае, но на данный момент это невозможно, если я хочу, чтобы он поддерживал UTF8. Второе решение тоже не сработает — обрежет 0, а барахло останется.
Есть идеи элегантного решения для С#?