Это популярный вопрос. Важно понимать, что задает автор вопроса, и что он отличается от того, что, вероятно, является наиболее распространенной потребностью. Чтобы воспрепятствовать неправильному использованию кода там, где он не нужен, я ответил первым позже.
Общая потребность
Каждая строка имеет набор символов и кодировку. Когда вы конвертируете объект System.String
в массив System.Byte
, у вас все еще есть набор символов и кодировка. В большинстве случаев вы знаете, какой набор символов и кодировку вам нужны, а .NET упрощает «копирование с преобразованием». Просто выберите соответствующий класс Encoding
.
// using System.Text;
Encoding.UTF8.GetBytes(".NET String to byte array")
Преобразование может потребоваться для обработки случаев, когда целевой набор символов или кодировка не поддерживает символ, находящийся в источнике. У вас есть несколько вариантов: исключение, замена или пропуск. Политика по умолчанию заменяет «?».
// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100"));
// -> "You win ?100"
Очевидно, что конверсии не обязательно без потерь!
Примечание. Для System.String
исходным набором символов является Unicode.
Единственная запутанная вещь заключается в том, что .NET использует имя набора символов для имени одной конкретной кодировки этого набора символов. Encoding.Unicode
должен называться Encoding.UTF16
.
Вот и все для большинства случаев. Если это то, что вам нужно, прекратите читать здесь. См. Забавную статью Джоэла Спольски , если вы не понимаете, что такое кодировка.
Конкретная потребность
Теперь автор вопроса спрашивает: «Каждая строка хранится в виде массива байтов, верно? Почему я не могу просто иметь эти байты?»
Он не хочет никакого преобразования.
Из C # spec :
Обработка символов и строк в C # использует кодировку Unicode. Чарс
тип представляет единицу кода UTF-16, а тип строки представляет
последовательность кодовых единиц UTF-16.
Итак, мы знаем, что если мы запросим нулевое преобразование (т.е. из UTF-16 в UTF-16), мы получим желаемый результат:
Encoding.Unicode.GetBytes(".NET String to byte array")
Но чтобы избежать упоминания о кодировках, мы должны сделать это по-другому. Если промежуточный тип данных приемлем, для этого есть концептуальное сокращение:
".NET String to byte array".ToCharArray()
Это не дает нам желаемый тип данных, но Ответ Мехрдада показывает, как преобразовать этот массив Char в байтовый массив, используя BlockCopy . Тем не менее, это копирует строку дважды! И он слишком явно использует специфический для кодирования код: тип данных System.Char
.
Единственный способ получить действительные байты, в которых хранится строка, - это использовать указатель. Оператор fixed
позволяет получить адрес значений. Из спецификации C #:
[Для] выражения типа string ... инициализатор вычисляет
адрес первого символа в строке.
Для этого компилятор пишет код, пропускающий другие части строкового объекта с помощью RuntimeHelpers.OffsetToStringData
. Итак, чтобы получить необработанные байты, просто создайте указатель на строку и скопируйте необходимое количество байтов.
// using System.Runtime.InteropServices
unsafe byte[] GetRawBytes(String s)
{
if (s == null) return null;
var codeunitCount = s.Length;
/* We know that String is a sequence of UTF-16 codeunits
and such codeunits are 2 bytes */
var byteCount = codeunitCount * 2;
var bytes = new byte[byteCount];
fixed(void* pRaw = s)
{
Marshal.Copy((IntPtr)pRaw, bytes, 0, byteCount);
}
return bytes;
}
Как указал @CodesInChaos, результат зависит от порядкового номера машины. Но автора вопроса это не касается.