Как преобразовать string в byte[] в .NET (C #) без указания конкретной кодировки вручную?

Я собираюсь зашифровать строку. Я могу зашифровать его без преобразования, но я все же хотел бы знать, почему здесь применяется кодирование.

Кроме того, почему вообще следует учитывать кодировку? Разве я не могу просто узнать, в каких байтах была сохранена строка? Почему существует зависимость от кодировок символов?

2333
Agnel Kurian 23 Янв 2009 в 16:39
27
Каждая строка хранится как массив байтов, верно? Почему я не могу просто получить эти байты?
 – 
Agnel Kurian
23 Янв 2009 в 17:05
147
Кодировка - это то, что отображает символы в байты. Например, в ASCII буква «A» соответствует числу 65. В другой кодировке это может быть не то же самое. Однако высокоуровневый подход к строкам, принятый в .NET framework, делает это в значительной степени несущественным (кроме этого случая).
 – 
Lucas Jones
13 Апр 2009 в 18:13
22
Чтобы сыграть защитника дьявола: если вы хотите получить байты строки в памяти (поскольку .NET использует их) и каким-то образом ими манипулировать (например, CRC32), и НИКОГДА не хотели декодировать их обратно в исходную строку ... Непонятно, зачем вам нужны кодировки или как вы выбираете, какую из них использовать.
 – 
Greg
1 Дек 2009 в 22:47
88
Удивлен, что никто еще не дал эту ссылку: joelonsoftware.com/articles/Unicode.html
 – 
Bevan
29 Июн 2010 в 06:57
34
Символ - это не байт, а байт - это не символ. Символ - это ключ к таблице шрифтов и лексической традиции. Строка - это последовательность символов. (Слова, абзацы, предложения и заголовки также имеют свои собственные лексические традиции, которые оправдывают их собственные определения типов, но я отвлекаюсь). Как и целые числа, числа с плавающей запятой и все остальное, символы кодируются в байтах. Было время, когда кодировка была простой: ASCII. Однако, чтобы вместить всю человеческую символику, 256 перестановок байта было недостаточно, и были разработаны кодировки для выборочного использования большего количества байтов.
 – 
George
28 Авг 2014 в 19:43

30 ответов

Лучший ответ

Вопреки приведенным здесь ответам, вам НЕ нужно беспокоиться о кодировании если байты не нужно интерпретировать!

Как вы упомянули, ваша цель - просто «узнать, в каких байтах была сохранена строка» .
(И, конечно же, чтобы иметь возможность воссоздать строку из байтов.)

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

Просто сделайте это вместо этого:

static byte[] GetBytes(string str)
{
    byte[] bytes = new byte[str.Length * sizeof(char)];
    System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
    return bytes;
}

// Do NOT use on arbitrary bytes; only use on GetBytes's output on the SAME system
static string GetString(byte[] bytes)
{
    char[] chars = new char[bytes.Length / sizeof(char)];
    System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
    return new string(chars);
}

Пока ваша программа (или другие программы) не пытается каким-то образом интерпретировать байты, о чем вы, очевидно, не упоминали, что собираетесь делать, то ничего неправильно с таким подходом! Беспокойство о кодировках просто усложняет вашу жизнь без реальной причины.

Дополнительное преимущество этого подхода: не имеет значения, содержит ли строка недопустимые символы, потому что вы все равно можете получить данные и восстановить исходную строку!

Он будет кодироваться и декодироваться точно так же, потому что вы просто смотрите на байты .

Однако, если бы вы использовали определенную кодировку, у вас возникли бы проблемы с кодированием / декодированием недопустимых символов.

1928
user541686 11 Июн 2022 в 06:13
270
Что неприятно в этом, так это то, что GetString и GetBytes должны выполняться в системе с одинаковым порядком байтов для работы. Таким образом, вы не можете использовать это для получения байтов, которые хотите преобразовать в строку в другом месте. Так что мне трудно придумать ситуации, в которых я бы хотел это использовать.
 – 
CodesInChaos
13 Май 2012 в 15:14
72
@CodeInChaos: Как я уже сказал, вся суть в том, что вы хотите использовать его в той же системе, с тем же набором функций. Если нет, то использовать его не стоит.
 – 
user541686
13 Май 2012 в 22:00
213
-1 Я гарантирую, что кто-то (кто не понимает байты против символов) захочет преобразовать свою строку в байтовый массив, он погуглиет и прочитает этот ответ, и они сделают неправильный поступок, потому что почти во всех случаях кодировка ЯВЛЯЕТСЯ релевантной.
 – 
artbristol
15 Июн 2012 в 15:07
427
@artbristol: Если они не хотят читать ответ (или другие ответы ...), то извините, тогда для меня нет лучшего способа общаться с ними. Я обычно предпочитаю отвечать на OP, а не пытаться угадать, что другие могут сделать с моим ответом - OP имеет право знать, и то, что кто-то может злоупотребить ножом, не означает, что нам нужно спрятать все ножи в мире для себя. Хотя, если вы не согласны, это тоже нормально.
 – 
user541686
15 Июн 2012 в 18:04
202
Этот ответ неверен на многих уровнях, но, прежде всего, из-за того, что он говорит: «Вам НЕ нужно беспокоиться о кодировании!». Два метода, GetBytes и GetString, излишни, поскольку они просто повторные реализации того, что уже делают Encoding.Unicode.GetBytes () и Encoding.Unicode.GetString (). Утверждение «Пока ваша программа (или другие программы) не пытается интерпретировать байты» также в корне ошибочны, поскольку неявно они означают, что байты следует интерпретировать как Unicode.
 – 
David
11 Июл 2012 в 16:36

Это зависит от кодировки вашей строки (ASCII, UTF-8, ...).

Например:

byte[] b1 = System.Text.Encoding.UTF8.GetBytes (myString);
byte[] b2 = System.Text.Encoding.ASCII.GetBytes (myString);

Небольшой пример того, почему кодирование имеет значение:

string pi = "\u03a0";
byte[] ascii = System.Text.Encoding.ASCII.GetBytes (pi);
byte[] utf8 = System.Text.Encoding.UTF8.GetBytes (pi);

Console.WriteLine (ascii.Length); //Will print 1
Console.WriteLine (utf8.Length); //Will print 2
Console.WriteLine (System.Text.Encoding.ASCII.GetString (ascii)); //Will print '?'

ASCII просто не приспособлен для работы со специальными символами.

Внутри .NET framework использует UTF-16 для представления строк, поэтому, если вы просто хотите чтобы получить точные байты, которые использует .NET, используйте System.Text.Encoding.Unicode.GetBytes (...).

См. Кодировка символов в .NET Framework (MSDN) для получения дополнительной информации.

1138
Peter Mortensen 24 Апр 2015 в 12:52
15
Но почему следует учитывать кодировку? Почему я не могу просто получить байты, не видя, какая кодировка используется? Даже если бы это было необходимо, не должен ли сам объект String знать, какая кодировка используется, и просто выгружать то, что находится в памяти?
 – 
Agnel Kurian
23 Янв 2009 в 16:48
65
Строки .NET всегда кодируются как Unicode. Так что используйте System.Text.Encoding.Unicode.GetBytes (); чтобы получить набор байтов, который .NET будет использовать для представления символов. Однако зачем вам это нужно? Я рекомендую UTF-8, особенно когда большинство символов находятся в западном латинском наборе.
 – 
AnthonyWJones
23 Янв 2009 в 17:33
8
Также: точные байты, используемые внутри строки не имеют значения , если система, которая их извлекает, не обрабатывает эту кодировку или обрабатывает ее как неправильную. Если все внутри .Net, зачем вообще конвертировать в массив байтов. В противном случае лучше указать свою кодировку
 – 
Joel Coehoorn
23 Янв 2009 в 18:42
13
@Joel, будьте осторожны с System.Text.Encoding.Default, так как он может отличаться на каждой машине, на которой он запущен. Поэтому рекомендуется всегда указывать кодировку, например UTF-8.
 – 
Ash
28 Янв 2010 в 12:01
26
Вам не нужны кодировки, если вы (или кто-то другой) на самом деле не намереваетесь интерпретировать данные, вместо того, чтобы рассматривать их как общий «блок байтов». Для таких вещей, как сжатие, шифрование и т. Д., Беспокоиться о кодировании бессмысленно. См. мой ответ, чтобы узнать, как это сделать, не беспокоясь о кодировке. (Я мог бы поставить -1 за то, что сказал, что вам нужно беспокоиться о кодировках, когда вы этого не делаете, но сегодня я не чувствую себя особенно злым .: P)
 – 
user541686
30 Апр 2012 в 11:55

Принятый ответ очень и очень сложен. Используйте для этого включенные классы .NET:

const string data = "A string with international characters: Norwegian: ÆØÅæøå, Chinese: 喂 谢谢";
var bytes = System.Text.Encoding.UTF8.GetBytes(data);
var decoded = System.Text.Encoding.UTF8.GetString(bytes);

Не изобретайте велосипед, если вам не нужно ...

302
Vlad 23 Июл 2015 в 17:32
16
В случае изменения принятого ответа для записи, это ответ Мехрдада в текущее время и дату. Надеюсь, ОП пересмотрит это и примет лучшее решение.
 – 
Thomas Eding
27 Сен 2013 в 22:20
9
В принципе хорошо, но кодировка должна быть System.Text.Encoding.Unicode, чтобы быть эквивалентной ответу Мехрдада.
 – 
Jodrell
25 Ноя 2014 в 12:08
7
С момента первоначального ответа вопрос редактировался бесчисленное количество раз, так что, возможно, мой ответ немного устарел. Я никогда не намеревался придавать обострение, эквивалентное ответу Мехрдада, но предлагаю разумный способ сделать это. Но, возможно, ты прав. Однако фраза «получить, в каких байтах была сохранена строка» в исходном вопросе очень неточна. Где хранится? В памяти? На диске? Если в памяти, System.Text.Encoding.Unicode.GetBytes, вероятно, будет более точным.
 – 
Erik A. Brandstadmoen
26 Ноя 2014 в 14:36
8
@AMissico, ваше предложение ошибочно, если вы не уверены, что ваша строка совместима с кодировкой по умолчанию вашей системы (строка, содержащая только символы ASCII в устаревшей кодировке по умолчанию вашей системы). Но нигде ОП не заявляет об этом.
 – 
Frédéric
6 Апр 2016 в 23:53
6
Однако это может привести к тому, что программа будет давать разные результаты в разных системах . Это никогда не хорошо. Даже если это для создания хэша или чего-то подобного (я предполагаю, что OP означает «шифрование»), одна и та же строка всегда должна давать один и тот же хеш.
 – 
Nyerguds
22 Апр 2016 в 13:33
BinaryFormatter bf = new BinaryFormatter();
byte[] bytes;
MemoryStream ms = new MemoryStream();

string orig = "喂 Hello 谢谢 Thank You";
bf.Serialize(ms, orig);
ms.Seek(0, 0);
bytes = ms.ToArray();

MessageBox.Show("Original bytes Length: " + bytes.Length.ToString());

MessageBox.Show("Original string Length: " + orig.Length.ToString());

for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo encrypt
for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo decrypt

BinaryFormatter bfx = new BinaryFormatter();
MemoryStream msx = new MemoryStream();            
msx.Write(bytes, 0, bytes.Length);
msx.Seek(0, 0);
string sx = (string)bfx.Deserialize(msx);

MessageBox.Show("Still intact :" + sx);

MessageBox.Show("Deserialize string Length(still intact): " 
    + sx.Length.ToString());

BinaryFormatter bfy = new BinaryFormatter();
MemoryStream msy = new MemoryStream();
bfy.Serialize(msy, sx);
msy.Seek(0, 0);
byte[] bytesy = msy.ToArray();

MessageBox.Show("Deserialize bytes Length(still intact): " 
   + bytesy.Length.ToString());
120
Michael Buen 26 Янв 2009 в 09:29
2
Вы можете использовать один и тот же экземпляр BinaryFormatter для всех этих операций.
 – 
Joel Coehoorn
23 Янв 2009 в 20:25
3
Очень интересно. По-видимому, он потеряет любой высокий суррогатный символ Unicode. См. Документацию по [BinaryFormatter]
 – 
user334911
18 Ноя 2010 в 21:51

Это популярный вопрос. Важно понимать, что задает автор вопроса, и что это отличается от наиболее распространенной потребности. Чтобы предотвратить неправильное использование кода там, где он не нужен, я сначала ответил на последнее.

Общая потребность

Каждая строка имеет набор символов и кодировку. Когда вы конвертируете объект 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 #:

При обработке символов и строк в C # используется кодировка Unicode. Тип char представляет собой кодовую единицу 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 #:

[Для] выражения типа строка, ... инициализатор вычисляет адрес первого символа в строке.

Для этого компилятор записывает код, пропускающий другие части строкового объекта с помощью 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, результат зависит от порядка байтов машины. Но автора вопроса это не волнует.

100
Community 23 Май 2017 в 13:31
4
Это правильно, но длина строки уже дает количество кодовых единиц (не кодовых точек).
 – 
Tom Blodget
4 Фев 2014 в 06:35
1
Спасибо что подметил это! Из MSDN: «Свойство Length [of String]] возвращает количество объектов Char в этом экземпляре, а не количество символов Юникода». Таким образом, ваш примерный код верен в том виде, в котором он написан.
 – 
Jan Hettich
4 Фев 2014 в 09:42
1
«Тип char представляет собой кодовую единицу UTF-16, а строковый тип представляет последовательность кодовых единиц UTF-16». --_ Спецификация C # 5. _ Хотя да, нет ничего, что могло бы предотвратить недопустимую строку Unicode: new String(new []{'\uD800', '\u0030'})
 – 
Tom Blodget
13 Ноя 2014 в 03:15
1
@TomBlodget: Интересно, что если взять экземпляры Globalization.SortKey, извлечь KeyData и упаковать полученные байты из каждого в String [два байта на символ, сначала MSB < / i>], вызов String.CompareOrdinal для результирующих строк будет значительно быстрее, чем вызов SortKey.Compare для экземпляров SortKey или даже вызов memcmp для этих экземпляров. Учитывая это, мне интересно, почему KeyData возвращает Byte[], а не String?
 – 
supercat
13 Ноя 2014 в 20:56
2
Увы, правильный ответ, но слишком поздно, никогда не наберет столько голосов, сколько принято. Из-за TL; DR люди подумают, что принятый ответ потрясает. копиенпастить и проголосовать за него.
 – 
Martin Capodici
30 Июн 2015 в 05:38

Вам необходимо принять во внимание кодировку, потому что 1 символ может быть представлен 1 или более байтами (примерно до 6), а разные кодировки будут обрабатывать эти байты по-разному.

У Джоэла есть сообщение об этом:

Абсолютный минимум Каждый разработчик программного обеспечения должен абсолютно точно знать о юникоде и наборах символов (никаких оправданий!)

99
Zhaph - Ben Duguid 23 Янв 2009 в 17:03
7
«1 символ может быть представлен 1 или более байтами» Я согласен. Мне просто нужны эти байты независимо от того, в какой кодировке находится строка. Единственный способ сохранить строку в памяти - это байты. Даже символы хранятся в 1 или более байтах. Я просто хочу заполучить их байты.
 – 
Agnel Kurian
23 Янв 2009 в 17:07
17
Вам не нужны кодировки, если вы (или кто-то другой) на самом деле не намереваетесь интерпретировать данные, вместо того, чтобы рассматривать их как общий «блок байтов». Для таких вещей, как сжатие, шифрование и т. Д., Беспокоиться о кодировании бессмысленно. См. мой ответ, чтобы узнать, как это сделать, не беспокоясь о кодировке.
 – 
user541686
30 Апр 2012 в 11:54
9
- В целом, но исходный вопрос, как было сказано, когда я изначально отвечал, не оговаривал, какой OP должен был произойти с этими байтами после того, как они их преобразовали, и для будущих поисковиков информация, относящаяся к этому, имеет отношение - это покрывается Ответ Джоэла довольно хорошо - и, как вы заявляете в своем ответе: при условии, что вы придерживаетесь мира .NET и используете свои методы конвертировать в / из, вы счастливы. Как только вы выйдете за рамки этого, значение будет иметь кодировка.
 – 
Zhaph - Ben Duguid
30 Апр 2012 в 14:48
1
Одна кодовая точка может быть представлена ​​до 4 байтами. (Одна кодовая единица UTF-32, суррогатная пара UTF-16 или 4 байта UTF-8.) Значения, для которых UTF-8 потребуется более 4 байтов, находятся за пределами диапазона 0x0..0x10FFFF Unicode. ;-)
 – 
DevSolar
8 Окт 2018 в 18:05

На первую часть вашего вопроса (как получить байты) уже ответили другие: посмотрите в пространство имен System.Text.Encoding.

Я отвечу на ваш дополнительный вопрос: зачем вам выбирать кодировку? Почему вы не можете получить это из самого строкового класса?

Ответ состоит из двух частей.

Прежде всего, байты, используемые внутри строкового класса не имеют значения , и всякий раз, когда вы предполагаете, что это так, вы, вероятно, вносите ошибку.

Если ваша программа полностью находится в мире .Net, вам вообще не нужно беспокоиться о получении байтовых массивов для строк, даже если вы отправляете данные по сети. Вместо этого используйте .Net Serialization, чтобы беспокоиться о передаче данных. Вам больше не нужно беспокоиться о фактических байтах: форматтер сериализации сделает это за вас.

С другой стороны, что, если вы отправляете эти байты куда-то, что вы не можете гарантировать, что они получат данные из сериализованного потока .Net? В этом случае вам определенно нужно беспокоиться о кодировании, потому что, очевидно, эта внешняя система заботится. Итак, внутренние байты, используемые строкой, не имеют значения: вам нужно выбрать кодировку, чтобы вы могли явно указать эту кодировку на принимающей стороне, даже если это та же самая кодировка, которая используется внутри .Net.

Я понимаю, что в этом случае вы можете предпочесть использовать фактические байты, хранящиеся в строковой переменной в памяти, где это возможно, с мыслью, что это может сэкономить некоторую работу при создании вашего потока байтов. Однако я говорю вам, что это просто не важно по сравнению с тем, чтобы убедиться, что ваш вывод понят на другом конце, и чтобы гарантировать, что вы должны явно указывать свою кодировку. Кроме того, если вы действительно хотите согласовать свои внутренние байты, вы уже можете просто выбрать кодировку Unicode и получить экономию производительности.

Это подводит меня ко второй части ... выбор кодировки Unicode сообщает .Net использовать базовые байты. Вам действительно нужно выбрать эту кодировку, потому что, когда выйдет какой-нибудь новомодный Unicode-Plus, среда выполнения .Net должна быть свободной, чтобы использовать эту новую, лучшую модель кодирования, не нарушая вашу программу. Но на данный момент (и в обозримом будущем) простой выбор кодировки Unicode дает вам то, что вы хотите.

Также важно понимать, что ваша строка должна быть переписана в провод, и это включает в себя хотя бы некоторую трансляцию битового шаблона , даже если вы используете соответствующую кодировку . Компьютер должен учитывать такие вещи, как Big vs Little Endian, сетевой порядок байтов, пакетирование, информацию о сеансе и т. Д.

50
Joel Coehoorn 26 Сен 2017 в 00:13
10
В .NET есть области, где вам действительно нужно получать байтовые массивы для строк. Многие классы .NET Cryptrography содержат такие методы, как ComputeHash (), которые принимают байтовый массив или поток. У вас нет альтернативы, кроме как сначала преобразовать строку в массив байтов (выбрав кодировку), а затем, при желании, обернуть ее в поток. Однако пока вы выбираете кодировку (например, UTF8) и используете ее, проблем с этим нет.
 – 
Ash
28 Янв 2010 в 12:33
Когда я не знал, что такое кодирование, и отказался узнать об этом из-за лени, я был точно в том же настроении, что и ОП (просто дайте мне уже байты...) Ваш ответ: первый (среди первых), кто позаботится о том, чтобы дать четкое предупреждение. Я был просто счастлив писать и читать двоичные файлы на своем ПК... пока мне не пришлось иметь дело с пользователями MAC/Linux, сетью, обновлением приложений до последней версии ОС, пониманием порядкового байта, пользовательскими кодировками (электронное ПЗУ и данные). В тот день, когда .Net будет кодировать Unicode с 4 байтами, UTF8 до 8 байтов. Я усвоил трудный способ избежать обхода собственных методов, когда это возможно.
 – 
Karl Stephen
12 Апр 2022 в 20:59

Чтобы продемонстрировать, что звуковой ответ Мехрдрада работает, его подход может даже сохранить непарные суррогатные символы (многие из которых высказывались против мой ответ, но в котором все одинаково виноваты, например System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; эти методы кодирования не могут, например, сохранять высокие суррогатные символы d800, а они просто заменяют высокие суррогатные символы со значением fffd):

using System;

class Program
{     
    static void Main(string[] args)
    {
        string t = "爱虫";            
        string s = "Test\ud800Test"; 

        byte[] dumpToBytes = GetBytes(s);
        string getItBack = GetString(dumpToBytes);

        foreach (char item in getItBack)
        {
            Console.WriteLine("{0} {1}", item, ((ushort)item).ToString("x"));
        }    
    }

    static byte[] GetBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }

    static string GetString(byte[] bytes)
    {
        char[] chars = new char[bytes.Length / sizeof(char)];
        System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
        return new string(chars);
    }        
}

Выход:

T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74

Попробуйте это с помощью System.Text.Encoding.UTF8.GetBytes или System.Text.Encoding.Unicode.GetBytes , они просто заменят высокие суррогатные символы значением fffd

Каждый раз, когда в этом вопросе происходит движение, я все еще думаю о сериализаторе (будь то от Microsoft или от стороннего компонента), который может сохранять строки, даже если он содержит непарные суррогатные символы; Я то и дело гуглил: сериализация непарного суррогатного символа .NET . Это не заставляет меня терять сон, но это немного раздражает, когда время от времени кто-то комментирует мой ответ, что он ошибочен, но их ответы одинаково ошибочны, когда речь идет о непарных суррогатных персонажах.

Черт, Microsoft должна была просто использовать System.Buffer.BlockCopy в своем BinaryFormatter

谢谢!

44
5 revs 23 Май 2017 в 15:18
3
Разве суррогаты не должны появляться парами, чтобы сформировать действительные кодовые точки? Если это так, я могу понять, почему данные будут искажены.
 – 
dtanders
14 Июн 2012 в 18:27
1
Да, я тоже об этом думаю, они должны появляться парами, непарные суррогатные символы просто появляются, если вы намеренно помещаете их в строку и делаете их непарными. Я не знаю, почему другие разработчики продолжают твердить, что вместо этого мы должны использовать подход с учетом кодирования, поскольку они считали подход сериализации (мой ответ, который был принятым ответом более 3 лет) не сохраняет непарный суррогатный символ неповрежденным. Но они забыли проверить, что их решения с поддержкой кодирования не сохраняют и непарный суррогатный символ, ирония судьбы ツ
 – 
Michael Buen
15 Июн 2012 в 03:23
Если есть библиотека сериализации, которая использует System.Buffer.BlockCopy внутри, все аргументы сторонников кодирования будут спорными
 – 
Michael Buen
15 Июн 2012 в 03:23
2
Мне кажется, что главная проблема в том, что вы большими жирными буквами говорите, что что-то не имеет значения, а не говорите, что в их случае это не имеет значения. В результате вы поощряете людей, которые смотрят на ваш ответ, совершать базовые ошибки программирования, которые в будущем вызовут разочарование у других. Непарные суррогаты недопустимы в строке. Это не массив символов, поэтому имеет смысл, что преобразование строки в другой формат приведет к ошибке FFFD для этого символа. Если вы хотите выполнять манипуляции со строкой вручную, используйте char [], как рекомендуется.
 – 
Trisped
11 Ноя 2014 в 23:06
3
@dtanders: A System.String - неизменная последовательность Char; .NET всегда позволял создавать объект String из любого Char[] и экспортировать его содержимое в Char[], содержащий те же значения, даже если исходный Char[] содержит непарные суррогаты .
 – 
supercat
13 Ноя 2014 в 00:57

Попробуйте это, намного меньше кода:

System.Text.Encoding.UTF8.GetBytes("TEST String");
41
Peter Mortensen 24 Апр 2015 в 12:58
Тогда попробуйте это System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép); и плачьте! Это будет работать, но System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Length пока "Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
 – 
mg30rg
5 Дек 2017 в 19:30
9
@ mg30rg: Как вы думаете, почему ваш пример странный? Конечно, в кодировке с переменной шириной не все символы имеют одинаковую длину байта. Что с этим не так?
 – 
Vlad
25 Фев 2018 в 04:18
Однако более верный комментарий здесь заключается в том, что как закодированные символы юникода (то есть как байты) символы, которые включают свои собственные диакритические знаки, дадут другой результат, чем диакритические знаки, разделенные на символы-модификаторы добавлены к персонажу. Но iirc есть методы в .net, чтобы специально разделить их, чтобы позволить получить последовательное представление байтов.
 – 
Nyerguds
31 Мар 2020 в 15:43

Что ж, я прочитал все ответы, и они касались использования кодирования или сериализации, которая отбрасывает непарные суррогаты.

Плохо, когда строка, например, исходит из SQL Server, где она была построена из массива байтов. хранение, например, хэша пароля. Если мы что-нибудь из него отбросим, ​​он сохранит недействительный хэш, а если мы хотим сохранить его в XML, мы хотим оставить его нетронутым (потому что модуль записи XML отбрасывает исключение для любого найденного непарного суррогата).

Поэтому в таких случаях я использую кодировку массивов байтов Base64, но в Интернете есть только одно решение этой проблемы на C #, и в нем есть ошибка, и это только один способ, поэтому я исправил ошибку и написал обратно процедуру. Вот вы, будущие гуглеры:

public static byte[] StringToBytes(string str)
{
    byte[] data = new byte[str.Length * 2];
    for (int i = 0; i < str.Length; ++i)
    {
        char ch = str[i];
        data[i * 2] = (byte)(ch & 0xFF);
        data[i * 2 + 1] = (byte)((ch & 0xFF00) >> 8);
    }

    return data;
}

public static string StringFromBytes(byte[] arr)
{
    char[] ch = new char[arr.Length / 2];
    for (int i = 0; i < ch.Length; ++i)
    {
        ch[i] = (char)((int)arr[i * 2] + (((int)arr[i * 2 + 1]) << 8));
    }
    return new String(ch);
}
25
Tshilidzi Mudau 9 Мар 2017 в 11:55
Вместо того, чтобы использовать свой собственный метод для преобразования массива байтов в base64, все, что вам нужно было сделать, это использовать встроенный конвертер: Convert.ToBase64String (arr);
 – 
Makotosan
10 Фев 2012 в 19:53
Спасибо, но я использовал Convert.ToBase64String(arr); для преобразования base64 byte[] (data) <-> string (serialized data to store in XML file). Но чтобы получить начальный byte[] (data), мне нужно было что-то сделать с String, содержащим двоичные данные (так MSSQL вернул их мне). ТАК, что приведенные выше функции предназначены для String (binary data) <-> byte[] (easy accessible binary data).
 – 
Gman
6 Мар 2012 в 23:15

Также объясните, почему следует учитывать кодировку. Разве я не могу просто узнать, в каких байтах была сохранена строка? Откуда такая зависимость от кодировки? !!!

Потому что не существует такого понятия, как «байты строки».

Строка (или, в более общем смысле, текст) состоит из символов: букв, цифр и других символов. Это все. Однако компьютеры ничего не знают о персонажах; они могут обрабатывать только байты. Следовательно, если вы хотите сохранить или передать текст с помощью компьютера, вам необходимо преобразовать символы в байты. Как ты это делаешь? Вот где на сцену выходят кодировки.

Кодировка - это не что иное, как соглашение о переводе логических символов в физические байты. Самая простая и известная кодировка - это ASCII, и это все, что вам нужно, если вы пишете на английском языке. Для других языков вам понадобятся более полные кодировки, поскольку любой из вариантов Unicode является самым безопасным выбором в настоящее время.

Короче говоря, попытка «получить байты строки без использования кодировок» так же невозможна, как «написать текст без использования какого-либо языка».

Между прочим, я настоятельно рекомендую вам (и всем в этом отношении) прочитать эту небольшую мудрость: Абсолютный минимум, что каждый разработчик программного обеспечения должен абсолютно точно знать о Unicode и наборах символов (без оправданий!)

24
Konamiman 23 Окт 2015 в 09:19
2
Позвольте мне уточнить: для перевода слова hello world в физические байты использовалась кодировка. Поскольку строка хранится на моем компьютере, я уверен, что она должна храниться в байтах. Я просто хочу получить доступ к этим байтам, чтобы сохранить их на диске или по любой другой причине. Я не хочу интерпретировать эти байты. Поскольку я не хочу интерпретировать эти байты, необходимость в кодировке на этом этапе столь же неуместна, как необходимость телефонной линии для вызова printf.
 – 
Agnel Kurian
16 Июл 2009 в 19:30
3
Но опять же, нет концепции перевода текста в физические байты, если вы не используете кодировку. Конечно, компилятор каким-то образом хранит строки в памяти, но он просто использует внутреннюю кодировку, которую вы (или кто-либо, кроме разработчика компилятора) не знаете. Итак, что бы вы ни делали, вам нужна кодировка для получения физических байтов из строки.
 – 
Konamiman
22 Июл 2009 в 12:35
Куриан: Конечно, это правда, что строка имеет где-то кучу байтов, в которых хранится ее содержимое (например, UTF-16). Но есть веская причина, по которой вы не можете получить к нему доступ: строки неизменяемы, и если вы можете получить внутренний массив byte [], вы также можете изменить его. Это нарушает неизменяемость, которая имеет жизненно важное значение, поскольку несколько строк могут совместно использовать одни и те же данные. Использование кодировки UTF-16 для получения строки, вероятно, просто скопирует данные.
 – 
ollb
14 Май 2011 в 04:06
2
@Gnafoo, копию байтов подойдет.
 – 
Agnel Kurian
14 Май 2011 в 09:06

C # для преобразования string в массив byte:

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
22
John Smith 12 Авг 2016 в 21:39
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
18
gkrogers 23 Янв 2009 в 16:43
Но почему следует учитывать кодировку? Почему я не могу просто получить байты, не видя, какая кодировка используется? Даже если бы это было необходимо, не должен ли сам объект String знать, какая кодировка используется, и просто выгружать то, что находится в памяти?
 – 
Agnel Kurian
23 Янв 2009 в 16:46
5
Это не всегда работает. Некоторые специальные символы могут потеряться при использовании такого метода, который я нашел на собственном горьком опыте.
 – 
JB King
23 Янв 2009 в 20:14

Вы можете использовать следующий код для преобразования между строкой и байтовым массивом.

string s = "Hello World";

// String to Byte[]

byte[] byte1 = System.Text.Encoding.Default.GetBytes(s);

// OR

byte[] byte2 = System.Text.ASCIIEncoding.Default.GetBytes(s);

// Byte[] to string

string str = System.Text.Encoding.UTF8.GetString(byte1);
17
Jarvis Stark 9 Сен 2014 в 15:30
VUP это решило мою проблему (byte [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
 – 
r.hamd
9 Сен 2015 в 16:19

С появлением Span<T> выпущенного с В C # 7.2 канонический метод захвата нижележащего представления строки в памяти в управляемый массив байтов:

byte[] bytes = "rubbish_\u9999_string".AsSpan().AsBytes().ToArray();

Преобразование обратно не должно начинаться, потому что это означает, что вы на самом деле каким-то образом интерпретируете данные, но для полноты:

string s;
unsafe
{
    fixed (char* f = &bytes.AsSpan().NonPortableCast<byte, char>().DangerousGetPinnableReference())
    {
        s = new string(f);
    }
}

Имена NonPortableCast и DangerousGetPinnableReference должны служить аргументом в пользу того, что вам, вероятно, не следует этого делать.

Обратите внимание, что для работы с Span<T> требуется установить пакет NuGet System.Memory .

Тем не менее, актуальный исходный вопрос и последующие комментарии подразумевают, что базовая память не «интерпретируется» (что, как я полагаю, означает, что она не изменяется и не читается сверх необходимости записывать ее как есть), это указывает на то, что следует использовать некоторую реализацию класса Stream вместо того, чтобы вообще рассматривать данные как строки.

17
John Rasch 10 Янв 2018 в 23:21
new string(f) неверно, вам, по крайней мере, нужно использовать перегрузку конструктора, которая принимает явную длину, если вы хотите надеяться на возврат всех строк.
 – 
Ben Voigt
8 Дек 2020 в 02:09

Я не уверен, но я думаю, что строка хранит свою информацию как массив символов, что неэффективно с байтами. В частности, определение Char - «Представляет символ Юникода».

Возьмите этот пример образца:

String str = "asdf éß";
String str2 = "asdf gh";
EncodingInfo[] info =  Encoding.GetEncodings();
foreach (EncodingInfo enc in info)
{
    System.Console.WriteLine(enc.Name + " - " 
      + enc.GetEncoding().GetByteCount(str)
      + enc.GetEncoding().GetByteCount(str2));
}

Обратите внимание, что ответ Unicode составляет 14 байтов в обоих случаях, тогда как ответ UTF-8 составляет всего 9 байтов для первого и только 7 для второго.

Поэтому, если вы хотите, чтобы строка использовалась просто байтами, просто используйте Encoding.Unicode, но это будет неэффективно с пространством для хранения.

13
John Smith 12 Авг 2016 в 21:38
То есть, если ваша строка на английском языке - китайская, вам лучше с UTF-16.
 – 
Christoph
14 Фев 2021 в 12:00

Ключевой проблемой является то, что глиф в строке занимает 32 бита (16 бит для символьного кода), но у байта остается только 8 бит. Однозначного сопоставления не существует, если вы не ограничиваете себя строками, содержащими только символы ASCII. System.Text.Encoding имеет множество способов сопоставить строку с byte [], вам нужно выбрать тот, который позволяет избежать потери информации и который будет легко использовать вашему клиенту, когда ему нужно сопоставить byte [] обратно со строкой. .

Utf8 - популярная кодировка, она компактна и не требует потерь.

10
Hans Passant 23 Янв 2009 в 17:15
3
UTF-8 компактен только в том случае, если большинство ваших символов находятся в наборе символов английского языка (ASCII). Если бы у вас была длинная строка китайских символов, UTF-16 был бы более компактной кодировкой, чем UTF-8 для этой строки. Это связано с тем, что UTF-8 использует один байт для кодирования ASCII и 3 (или, может быть, 4) в противном случае.
 – 
Joel Mueller
23 Янв 2009 в 23:40
7
Правда. Но как вы можете не знать о кодировании, если вы знакомы с обработкой китайского текста?
 – 
Hans Passant
24 Янв 2009 в 06:40

Использование:

    string text = "string";
    byte[] array = System.Text.Encoding.UTF8.GetBytes(text);

Результат:

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
9
Peter Mortensen 9 Янв 2017 в 04:22
OP специально просит НЕ указывать кодировку ... "без указания конкретной кодировки вручную"
 – 
Ferdz
30 Авг 2018 в 16:40

Самый быстрый способ

public static byte[] GetBytes(string text)
{
    return System.Text.ASCIIEncoding.UTF8.GetBytes(text);
}

ИЗМЕНИТЬ как прокомментировал Макотосан, теперь это лучший способ:

Encoding.UTF8.GetBytes(text)
8
Alessandro Annini 4 Авг 2016 в 13:31
8
ASCIIEncoding ..... не нужен. Предпочтительно просто использовать Encoding.UTF8.GetBytes (text).
 – 
Makotosan
18 Фев 2012 в 00:40

Самый близкий подход к вопросу OP - это Tom Blodget's, который фактически входит в объект и извлекает байты. Я говорю «ближайший», потому что это зависит от реализации объекта String.

"Can't I simply get what bytes the string has been stored in?"

Конечно, но здесь возникает основная ошибка в вопросе. String - это объект, который может иметь интересную структуру данных. Мы уже знаем, что это так, потому что это позволяет хранить непарные суррогаты. Он может хранить длину. Он может хранить указатель на каждый из «парных» суррогатов, позволяющий быстро подсчитывать. И т.д. Все эти дополнительные байты не являются частью символьных данных.

Вам нужны байты каждого символа в массиве. И здесь на помощь приходит «кодировка». По умолчанию вы получаете UTF-16LE. Если вас не интересуют сами байты, за исключением обратного пути, вы можете выбрать любую кодировку, включая `` по умолчанию '', и преобразовать ее позже (предполагая те же параметры, как кодировка по умолчанию, кодовые точки, исправления ошибок , разрешенные вещи, такие как непарные суррогаты и т. д.

Но зачем оставлять «кодировку» на волю магии? Почему бы не указать кодировку, чтобы знать, какие байты вы получите?

"Why is there a dependency on character encodings?"

Кодировка (в этом контексте) просто означает байты, представляющие вашу строку. Не байты строкового объекта. Вы хотели байты, в которых хранилась строка - здесь вопрос был задан наивно. Вам нужны байты строки в непрерывном массиве, представляющем строку, а не все другие двоичные данные, которые может содержать строковый объект.

Это означает, что то, как хранится строка, не имеет значения. Вам нужна строка, «закодированная» в байты в байтовом массиве.

Мне нравится ответ Тома Блогета, потому что он направил вас в сторону «байтов строкового объекта». Однако это зависит от реализации, и, поскольку он заглядывает во внутреннее устройство, может быть трудно восстановить копию строки.

Ответ Мердада неверен, потому что он вводит в заблуждение на концептуальном уровне. У вас все еще есть список закодированных байтов. Его конкретное решение позволяет сохранить непарные суррогаты - это зависит от реализации. Его конкретное решение не создало бы байты строки точно, если бы GetBytes по умолчанию возвращал строку в UTF-8.


Я передумал по этому поводу (решение Мердада) - это не получение байтов строки; скорее он получает байты массива символов, который был создан из строки. Независимо от кодировки тип данных char в C # имеет фиксированный размер. Это позволяет создавать байтовый массив постоянной длины и воспроизводить массив символов на основе размера байтового массива. Таким образом, если бы кодировка была UTF-8, но каждый символ имел 6 байтов для размещения наибольшего значения utf8, он все равно работал бы. Так что действительно - кодировка символа не имеет значения.

Но использовалось преобразование - каждый символ помещался в поле фиксированного размера (тип символа C #). Однако не имеет значения, что это за представление, которое технически является ответом на OP. Итак - если вы все равно собираетесь конвертировать ... Почему бы не «кодировать»?

8
Gerard ONeill 1 Ноя 2017 в 22:44
Эти символы не поддерживаются в UTF-8, UTF-16 или даже UTF-32, например: 񩱠 & (Char) 55906 & (Char) 55655. Так что вы можете ошибаться, и ответ Мехрдада - безопасное преобразование без учета того, какие типы кодировок используются.
 – 
Mojtaba Rezaeian
11 Фев 2016 в 22:48
Raymon, символы уже представлены некоторым значением Unicode - и все значения Unicode могут быть представлены всеми UTF. Есть ли более подробное объяснение того, о чем вы говорите? В какой кодировке символов существуют эти два значения (или 3 ..)?
 – 
Gerard ONeill
11 Фев 2016 в 23:47
Это недопустимые символы, которые не поддерживаются никакими диапазонами кодирования. Это не означает, что они на 100% бесполезны. Код, преобразующий строку любого типа в ее эквивалент в массиве байтов, независимо от кодировки, вовсе не является неправильным решением и может иметь свое собственное использование в определенных случаях.
 – 
Mojtaba Rezaeian
12 Фев 2016 в 00:02
1
Хорошо, тогда я думаю, вы не понимаете проблему. Мы знаем, что это массив, совместимый с Unicode - на самом деле, поскольку это .net, мы знаем, что это UTF-16. Так что этих персонажей там не будет. Вы также не полностью прочитали мой комментарий об изменении внутренних представлений. Строка - это объект, а не закодированный байтовый массив. Так что я не согласен с вашим последним утверждением. Вы хотите, чтобы код преобразовывал все строки Юникода в любую кодировку UTF. Это делает то, что вы хотите, правильно.
 – 
Gerard ONeill
12 Фев 2016 в 01:17
Объекты - это последовательность данных, изначально последовательность битов, которые описывают объект в его текущем состоянии. Таким образом, все данные в языках программирования можно преобразовать в массив байтов (каждый байт определяет 8 бит), поскольку вам может потребоваться сохранить некоторое состояние любого объекта в памяти. Вы можете сохранить и сохранить последовательность байтов в файле или в памяти и преобразовать ее в целое число, bigint, изображение, строку Ascii, строку UTF-8, зашифрованную строку или ваш собственный определенный тип данных после чтения с диска. Таким образом, вы не можете сказать, что объекты - это нечто иное, чем последовательность байтов.
 – 
Mojtaba Rezaeian
12 Фев 2016 в 02:00

Как преобразовать строку в byte [] в .NET (C #) без указания конкретной кодировки вручную?

строка в .NET представляет текст как последовательность кодовых единиц UTF-16, поэтому байты уже закодированы в памяти в UTF-16.

Ответ Мехрдада

Вы можете использовать ответ Мердада, но на самом деле он использует кодировку, потому что символы - это UTF-16. Он вызывает ToCharArray, который при просмотре источника создает char[] и копирует память напрямую в него. Затем он копирует данные в массив байтов, который также выделяется. Таким образом, под капотом он копирует базовые байты дважды и выделяет массив символов, который не используется после вызова.

Ответ Тома Блоджета

Ответ Тома Блоджет на 20-30% быстрее, чем Mehrdad, поскольку он пропускает промежуточный этап выделения массива символов и копирования байтов. к нему, но для этого требуется, чтобы вы скомпилировали его с опцией /unsafe. Если вы абсолютно не хотите использовать кодировку, я думаю, что это выход. Если вы поместите свой логин для шифрования в блок fixed, вам даже не потребуется выделять отдельный массив байтов и копировать в него байты.

Кроме того, почему следует учитывать кодировку? Разве я не могу просто узнать, в каких байтах была сохранена строка? Почему существует зависимость от кодировок символов?

Потому что это правильный способ сделать это. string - это абстракция.

Использование кодировки может вызвать проблемы, если у вас есть «строки» с недопустимыми символами, но этого не должно происходить. Если вы вводите данные в свою строку с недопустимыми символами, вы делаете это неправильно. Вероятно, для начала вам следует использовать массив байтов или кодировку Base64.

Если вы используете System.Text.Encoding.Unicode, ваш код будет более устойчивым. Вам не нужно беспокоиться о порядке байтов системы, в которой будет выполняться ваш код. Вам не нужно беспокоиться, если в следующей версии CLR будет использоваться другая внутренняя кодировка символов.

Я думаю, вопрос не в том, почему вы хотите беспокоиться о кодировке, а в том, почему вы хотите игнорировать ее и использовать что-то еще. Кодирование предназначено для представления абстракции строки в последовательности байтов. System.Text.Encoding.Unicode предоставит вам кодировку порядка байтов с прямым порядком байтов и будет работать так же во всех системах, сейчас и в будущем.

8
Jason Goemaat 2 Июл 2018 в 23:51
На самом деле строка в C # НЕ ограничивается только UTF-16. Верно то, что он содержит вектор 16-битных кодовых единиц, но эти 16-битные кодовые единицы не ограничиваются допустимым UTF-16. Но поскольку они 16-битные, вам нужна кодировка (порядок байтов), чтобы преобразовать их в 8-битные. Затем строка может хранить данные не в Юникоде, включая двоичный код (например, растровое изображение). Он интерпретируется как UTF-16 только в средствах форматирования ввода-вывода и текста, которые выполняют такую ​​интерпретацию.
 – 
verdy_p
7 Сен 2019 в 18:42
Таким образом, в строке C # вы можете безопасно хранить кодовую единицу, такую ​​как 0xFFFF или 0xFFFE, даже если они не являются символами в UTF-16, и вы можете сохранить изолированный 0xD800, за которым не следует кодовая единица, в 0xDC00..0xDFFF (т.е. непарные суррогаты, недопустимые в UTF-16). То же самое относится к строкам в Javascript / ECMAscript и Java.
 – 
verdy_p
7 Сен 2019 в 18:47
Когда вы используете «GetBytes», вы, конечно, не указываете кодировку, но предполагаете порядок байтов, чтобы получить два байта в конкретном случае для каждой единицы кода, хранящейся локально в строке. Когда вы строите новую строку из байтов, вам также понадобится конвертер, не обязательно UTF-8 в UTF-16, вы можете вставить дополнительный 0 в старший байт или упаковать два байта (в первом порядке MSB или LSB в первом порядке) в тот же 16-битный кодовый блок. Строки тогда представляют собой компактную форму для массивов 16-битных целых чисел. Связь с «символами» - еще одна проблема, в C # они не являются фактическими типами, поскольку они по-прежнему представлены в виде строк.
 – 
verdy_p
7 Сен 2019 в 18:55

Вы можете использовать следующий код для преобразования string в byte array в .NET.

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
6
İlker Elçora 2 Май 2014 в 11:39

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

[DllImport(
        "msvcrt.dll",
        EntryPoint = "memcpy",
        CallingConvention = CallingConvention.Cdecl,
        SetLastError = false)]
private static extern unsafe void* UnsafeMemoryCopy(
    void* destination,
    void* source,
    uint count);

public static byte[] GetUnderlyingBytes(string source)
{
    var length = source.Length * sizeof(char);
    var result = new byte[length];
    unsafe
    {
        fixed (char* firstSourceChar = source)
        fixed (byte* firstDestination = result)
        {
            var firstSource = (byte*)firstSourceChar;
            UnsafeMemoryCopy(
                firstDestination,
                firstSource,
                (uint)length);
        }
    }

    return result;
}

Эта функция довольно быстро предоставит вам копию байтов, лежащих в основе вашей строки. Вы получите эти байты независимо от того, как они кодируются в вашей системе. Эта кодировка почти наверняка UTF-16LE, но это деталь реализации, о которой вам не нужно беспокоиться.

Было бы безопаснее, проще и надежнее просто позвонить,

System.Text.Encoding.Unicode.GetBytes()

По всей вероятности, это даст тот же результат, его легче набрать, и байты будут передаваться туда и обратно, как и представление байтов в Unicode, с вызовом

System.Text.Encoding.Unicode.GetString()
4
Jodrell 9 Дек 2020 в 15:18
Как упоминалось во многих других комментариях, Unicode.GetBytes() / Unicode.GetString() НЕ выполняет двусторонний цикл для всех экземпляров .NET string.
 – 
Ben Voigt
8 Дек 2020 в 02:13
@BenVoigt, я подправил ответ. В наши дни я бы сделал что-то менее специфичное для Windows.
 – 
Jodrell
9 Дек 2020 в 15:12
Вы можете подумать о том, чтобы избежать p / invoke для этого, Marshal.Copy отлично подойдет для копирования из указателя в массив байтов. stackoverflow.com/a/54453180/103167
 – 
Ben Voigt
9 Дек 2020 в 19:45
 – 
Jodrell
10 Дек 2020 в 11:39

Вот моя небезопасная реализация преобразования String в Byte[]:

public static unsafe Byte[] GetBytes(String s)
{
    Int32 length = s.Length * sizeof(Char);
    Byte[] bytes = new Byte[length];

    fixed (Char* pInput = s)
    fixed (Byte* pBytes = bytes)
    {
        Byte* source = (Byte*)pInput;
        Byte* destination = pBytes;

        if (length >= 16)
        {
            do
            {
                *((Int64*)destination) = *((Int64*)source);
                *((Int64*)(destination + 8)) = *((Int64*)(source + 8));

                source += 16;
                destination += 16;
            }
            while ((length -= 16) >= 16);
        }

        if (length > 0)
        {
            if ((length & 8) != 0)
            {
                *((Int64*)destination) = *((Int64*)source);

                source += 8;
                destination += 8;
            }

            if ((length & 4) != 0)
            {
                *((Int32*)destination) = *((Int32*)source);

                source += 4;
                destination += 4;
            }

            if ((length & 2) != 0)
            {
                *((Int16*)destination) = *((Int16*)source);

                source += 2;
                destination += 2;
            }

            if ((length & 1) != 0)
            {
                ++source;
                ++destination;

                destination[0] = source[0];
            }
        }
    }

    return bytes;
}

Это намного быстрее, чем принятый anwser, даже если он не такой элегантный, как он есть. Вот мои тесты секундомера за 10000000 итераций:

[Second String: Length 20]
Buffer.BlockCopy: 746ms
Unsafe: 557ms

[Second String: Length 50]
Buffer.BlockCopy: 861ms
Unsafe: 753ms

[Third String: Length 100]
Buffer.BlockCopy: 1250ms
Unsafe: 1063ms

Чтобы использовать его, вы должны отметить «Разрешить небезопасный код» в свойствах сборки вашего проекта. Согласно .NET Framework 3.5, этот метод также можно использовать как расширение String:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
3
John Smith 12 Авг 2016 в 21:38
Является ли значение RuntimeHelpers.OffsetToStringData кратным 8 в версиях .NET для Itanium? Потому что в противном случае это не удастся из-за невыровненных чтений.
 – 
Jon Hanna
6 Янв 2014 в 18:09
Не было бы проще вызвать memcpy? stackoverflow.com/a/27124232/659190
 – 
Jodrell
25 Ноя 2014 в 13:33

Когда вас спросят, что вы собираетесь делать с байтами, вы ответил:

Я собираюсь его зашифровать. Я могу зашифровать его без преобразования, но я все же хотел бы знать, почему здесь применяется кодирование. Просто дай мне байты, вот что я говорю.

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

  • Возможно, вам потребуется связаться с процессом, реализованным на другом языке или во время выполнения. (Это может включать, например, сервер, работающий на другом компьютере, или отправку строки клиенту браузера JavaScript.)
  • Программа может быть повторно реализована на другом языке или в другой среде выполнения в будущем.
  • Реализация .NET может изменить внутреннее представление строк. Вы можете подумать, что это звучит неправдоподобно, но это на самом деле произошло в Java 9, чтобы уменьшить использование памяти. Нет причин, по которым .NET не мог последовать их примеру. Скит предполагает, что UTF-16, вероятно, сегодня не является оптимальным, поэтому возникают эмодзи и другие блоки Unicode, требующие большего, чем 2 байта для представления, что увеличивает вероятность того, что внутреннее представление может измениться в будущем.

Для связи (либо с полностью разрозненным процессом, либо с той же программой в будущем) вам необходимо определить свой протокол строго , чтобы свести к минимуму сложность работы с ним или случайного создания ошибок. В зависимости от внутреннего представления .NET не существует строгого, ясного или даже гарантированно непротиворечивого определения. Стандартная кодировка - это строгое определение, которое не подведет в будущем.

Другими словами, вы не можете удовлетворить свои требования к согласованности , не указав кодировку.

Вы можете определенно использовать UTF-16 напрямую, если обнаружите, что ваш процесс работает значительно лучше, поскольку .NET использует его для внутренних целей или по любой другой причине, но вам нужно явно выбрать эту кодировку и выполнить эти преобразования. явно в вашем коде, а не в зависимости от внутренней реализации .NET.

Так что выберите кодировку и используйте ее:

using System.Text;

// ...

Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")

Как видите, на самом деле меньше кода просто использовать встроенные объекты кодирования, чем реализовывать собственные методы чтения / записи.

3
jpmc26 10 Окт 2021 в 19:20

Строка может быть преобразована в массив байтов несколькими способами из-за следующего факта: .NET поддерживает Unicode, а Unicode стандартизирует несколько разностных кодировок, называемых UTF. Они имеют разную длину байтового представления, но эквивалентны в том смысле, что, когда строка кодируется, она может быть закодирована обратно в строку, но если строка кодируется одним UTF и декодируется в предположении другого UTF, если это можно прикрутить вверх.

Кроме того, .NET поддерживает кодировки, отличные от Unicode, но они недействительны в общем случае (будут действительны только в том случае, если ограниченный поднабор кодовой точки Unicode используется в фактической строке, такой как ASCII). Внутри .NET поддерживает UTF-16, но для потокового представления обычно используется UTF-8. Это также де-факто стандарт для Интернета.

Неудивительно, что сериализация строки в массив байтов и десериализация поддерживается классом System.Text.Encoding, который является абстрактным классом; его производные классы поддерживают конкретные кодировки: ASCIIEncoding и четыре UTF (System.Text.UnicodeEncoding поддерживает UTF-16)

См. эту ссылку.

Для сериализации в массив байтов с помощью System.Text.Encoding.GetBytes. Для обратной операции используйте System.Text.Encoding.GetChars. Эта функция возвращает массив символов, поэтому для получения строки используйте конструктор строки System.String(char[]).
Ссылка на эту страницу.

Примере:

string myString = //... some string

System.Text.Encoding encoding = System.Text.Encoding.UTF8; //or some other, but prefer some UTF is Unicode is used
byte[] bytes = encoding.GetBytes(myString);

//next lines are written in response to a follow-up questions:

myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);
myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);

//how many times shall I repeat it to show there is a round-trip? :-)
2
Bharat Mane 17 Авг 2017 в 10:33

Это зависит от того, для чего вам нужны байты

Это потому, что, как очень точно сказал Тайлер, «Строки не являются чистыми данными. Они также содержат < a href = "http://www.diffen.com/difference/Data_vs_Information" rel = "nofollow noreferrer"> информация . " В этом случае информация представляет собой кодировку, которая была принята при создании строки.

Предполагая, что у вас есть двоичные данные (а не текст), хранящиеся в строке

Это основано на комментарии OP к его собственному вопросу, и это правильный вопрос, если я понимаю намеки OP на этот вариант использования.

Хранение двоичных данных в строках, вероятно, является неправильным подходом из-за предполагаемой кодировки, упомянутой выше! Какая бы программа или библиотека ни хранила эти двоичные данные в string (вместо массива byte[], который был бы более подходящим), битва уже проиграна еще до того, как она началась. Если они отправляют вам байты в запросе / ответе REST или в чем-то, что должно передавать строки, Base64 < / a> было бы правильным подходом.

Если у вас есть текстовая строка с неизвестной кодировкой

Все остальные ответили на этот неправильный вопрос неправильно.

Если строка выглядит хорошо как есть, просто выберите кодировку (желательно начинающуюся с UTF), используйте соответствующую функцию System.Text.Encoding.???.GetBytes() и сообщите тому, кому вы дадите байты, какую кодировку вы выбрали.

2
NH. 8 Ноя 2017 в 22:15

Если вы используете .NET Core или System.Memory для .NET Framework , существует очень эффективный механизм маршалинга, доступный через Span и Память , которые могут эффективно интерпретировать строковую память как диапазон байтов. Когда у вас есть диапазон байтов, вы можете маршалировать его обратно к другому типу или скопировать диапазон в массив для сериализации.

Подводя итог сказанному другими:

  • Хранение представления такого типа сериализации чувствительно к порядку байтов в системе, оптимизации компилятора и изменениям во внутреннем представлении строк в исполняющейся среде выполнения .NET.
    • Избегайте длительного хранения.
    • Избегайте десериализации или интерпретации строки в других средах. <ул>
    • Сюда входят другие компьютеры, архитектуры процессоров, среды выполнения .NET, контейнеры и т. д.
    • К ним относятся сравнения, форматирование, шифрование, обработка строк, локализация, преобразование символов и т. д.
  • Не делайте предположений о кодировке символов. <ул>
  • На практике кодировкой по умолчанию обычно является UTF-16LE, но компилятор/среда выполнения могут выбрать любое внутреннее представление

Реализация

public static class MarshalExtensions
{
   public static ReadOnlySpan<byte> AsBytes(this string value) => MemoryMarshal.AsBytes(value.AsSpan());
   public static string AsString(this ReadOnlySpan<byte> value) => new string(MemoryMarshal.Cast<byte, char>(value));
}

Примере

static void Main(string[] args)
{
    string str1 = "你好,世界";
    ReadOnlySpan<byte> span = str1.AsBytes();
    string str2 = span.AsString();

    byte[] bytes = span.ToArray();

    Debug.Assert(bytes.Length > 0);
    Debug.Assert(str1 == str2);
}

Furthur Insight

В C ++ это примерно эквивалентно reinterpret_cast , а в C это примерно эквивалентно приведению к системному типу слова ( char ).

В последних версиях .NET Core Runtime (CoreCLR) операции над промежутками эффективно вызывают встроенные функции компилятора и различные оптимизации, которые иногда могут исключать проверку границ, что приводит к исключительной производительности при сохранении безопасности памяти, предполагая, что ваша память был выделен средой CLR, и диапазоны не получены из указателей неуправляемого распределителя памяти.

Предостережения

При этом используется механизм, поддерживаемый CLR, который возвращает ReadOnlySpan из строки; Кроме того, этот диапазон не обязательно охватывает всю внутреннюю структуру строки. ReadOnlySpan подразумевает, что вы должны создать копию, если вам нужно выполнить мутацию, поскольку строки неизменяемы.

2
Chris Hutchinson 4 Авг 2020 в 00:35
Небольшой комментарий: вопреки распространенному мнению, вполне допустимым вариантом использования этого механизма является шифрование во время выполнения: извлечение байтового представления, шифрование байтов и сохранение зашифрованной полезной нагрузки в памяти. Это минимизирует накладные расходы на кодирование, и пока оно не сериализуется и не передается в другую среду, не будет возникать никаких проблем, связанных с кодированием, из-за семантики интерпретации или внутреннего представления. Есть аргумент в пользу использования SecureString для этой цели и опасения по поводу сборки мусора, но в остальном посылка кажется разумной.
 – 
Chris Hutchinson
4 Авг 2020 в 00:23
bytes[] buffer = UnicodeEncoding.UTF8.GetBytes(string something); //for converting to UTF then get its bytes

bytes[] buffer = ASCIIEncoding.ASCII.GetBytes(string something); //for converting to ascii then get its bytes
1
user1120193 2 Янв 2012 в 15:07

Просто используйте это:

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
1
jonsca 1 Июл 2015 в 04:14
2
... и потерять всех персонажей с прыжком выше 127. На моем родном языке вполне допустимо написать «Árvíztűr tükörfúrógép.». System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString(); вернет "Árvizturo tukörfurogép." информацию о потерях, которую невозможно восстановить. (И я еще не упомянул азиатские языки, где вы теряете все символы.)
 – 
mg30rg
11 Янв 2018 в 18:09