Как получить согласованное байтовое представление строк в C # без указания кодировки вручную?

2191

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

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

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

Агнель Курьян
источник
23
Каждая строка хранится как массив байтов, верно? Почему я не могу просто получить эти байты?
Агнель Курьян
135
Кодировка - это то, что отображает символы в байты. Например, в ASCII буква «А» соответствует номеру 65. В другой кодировке она может отличаться. Однако высокоуровневый подход к строкам, принятым в .NET Framework, делает это в значительной степени неактуальным (за исключением этого случая).
Лукас Джонс
20
Чтобы играть адвокат дьявола: Если вы хотите получить байты строки в памяти (как .NET использует их) и каким-то образом манипулировать ими (например, CRC32), и НИКОГДА никогда не хотел декодировать его обратно в исходную строку ... это Не ясно, почему вы заботитесь о кодировках или о том, какой вариант выбрать.
Грег
79
Удивлен, никто еще не дал эту ссылку: joelonsoftware.com/articles/Unicode.html
Беван
28
Символ - это не байт, а байт - это не символ. Символ - это и ключ к таблице шрифтов, и лексическая традиция. Строка - это последовательность символов. (Слова, абзацы, предложения и названия также имеют свои собственные лексические традиции, которые оправдывают их собственные определения типов - но я отвлекся). Как и целые числа, числа с плавающей точкой и все остальное, символы кодируются в байты. Было время, когда кодировка была проста один в один: ASCII. Однако, чтобы приспособить всю человеческую символику, 256 перестановок байта были недостаточны, и были разработаны кодировки, чтобы выборочно использовать больше байтов.
Джордж

Ответы:

1856

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

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

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

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

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);
}

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

Дополнительное преимущество этого подхода:

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

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

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

user541686
источник
248
Что некрасиво об этом один в том, что GetStringи GetBytesнужно выполняться на системе с одной и той же работы на порядок байтов. Таким образом, вы не можете использовать это, чтобы получить байты, которые вы хотите превратить в строку в другом месте. Поэтому мне трудно придумывать ситуации, в которых я хотел бы использовать это.
CodesInChaos
72
@CodeInChaos: Как я уже сказал, весь смысл в том, если вы хотите использовать его в той же системе, с тем же набором функций. Если нет, то вы не должны его использовать.
user541686
193
-1 Я гарантирую, что кто-то (кто не понимает байты против символов) захочет преобразовать свою строку в байтовый массив, он будет гуглить и читать этот ответ, и он сделает неправильную вещь, потому что почти во всех случаи, кодирование IS актуальны.
artbristol
401
@artbristol: Если они не могут потрудиться прочитать ответ (или другие ответы ...), то извините, но для меня нет лучшего способа с ними общаться. Обычно я предпочитаю отвечать на ФП, а не пытаться угадать, что другие могут сделать с моим ответом - ФП имеет право знать, и то, что кто-то может злоупотреблять ножом, не означает, что нам нужно спрятать все ножи в мире. для себя. Хотя, если вы не согласны, это тоже хорошо.
user541686 15.06.12
185
Этот ответ неверен на многих уровнях, но в первую очередь из-за того, что он гласит: «Вам не нужно беспокоиться о кодировании!». Два метода, GetBytes и GetString, излишни в той мере, в какой они просто являются повторными реализациями того, что уже делают Encoding.Unicode.GetBytes () и Encoding.Unicode.GetString (). Утверждение «Пока ваша программа (или другие программы) не пытаются интерпретировать байты» также в корне неверно, поскольку неявно они означают, что байты должны интерпретироваться как Unicode.
Дэвид
1108

Это зависит от кодировки вашей строки ( 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) для получения дополнительной информации.

bmotmans
источник
14
Но почему следует учитывать кодировку? Почему я не могу просто получить байты, не видя, какая кодировка используется? Даже если бы это было необходимо, разве сам объект String не должен знать, какая кодировка используется, и просто выгружать то, что находится в памяти?
Агнель Курьян
57
Строки .NET всегда кодируются как Unicode. Так что используйте System.Text.Encoding.Unicode.GetBytes (); чтобы получить набор байтов, который .NET использовал бы для представления символов. Однако зачем вам это? Я рекомендую UTF-8, особенно когда большинство символов в западном латинском наборе.
AnthonyWJones
8
Кроме того: точные байты, используемые внутри строки, не имеют значения, если система, которая их извлекает, не обрабатывает эту кодировку или обрабатывает ее как неправильную кодировку. Если это все в .Net, зачем вообще конвертировать в массив байтов. В противном случае лучше указывать кодировку явно
Джоэл Коухорн
11
@Joel, будьте осторожны с System.Text.Encoding.Default, так как он может отличаться на каждой машине, на которой он запущен. Вот почему рекомендуется всегда указывать кодировку, например UTF-8.
Эш
25
Вам не нужны кодировки, если только вы (или кто-то еще) на самом деле не намерены интерпретировать данные вместо того, чтобы рассматривать их как общий «блок байтов». Для таких вещей, как сжатие, шифрование и т. Д., Беспокоиться о кодировке не имеет смысла. Смотрите мой ответ, чтобы узнать, как это сделать, не беспокоясь о кодировке. (Я мог бы дать -1, если бы сказал, что вам нужно беспокоиться о кодировках, когда вы этого не делаете, но сегодня я не чувствую себя особенно
злым
285

Принятый ответ очень, очень сложный. Используйте для этого включенные классы .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);

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

Эрик А. Брандштадмоен
источник
14
В случае, если принятый ответ будет изменен, для целей записи, это ответ Mehrdad в это время и дату. Надеемся, что ОП еще раз рассмотрит это и примет лучшее решение.
Томас Эдинг
7
в принципе хорошо, но кодировка должна System.Text.Encoding.Unicodeбыть эквивалентна ответу Мерадада.
Джодрелл
5
Вопрос был отредактирован несколько раз с момента первоначального ответа, так что, возможно, мой ответ немного устарел. Я никогда не намеревался дать преувеличение, эквивалентное ответу Мехрдада, но дать разумный способ сделать это. Но вы можете быть правы. Однако фраза «получить, в каких байтах строка была сохранена» в исходном вопросе очень неточна. Хранится где? В памяти? На диске? Если бы в памяти, System.Text.Encoding.Unicode.GetBytesвероятно , было бы более точным.
Эрик А. Брандштадмоен
7
@AMissico, ваше предложение содержит ошибки, если только вы не уверены, что ваша строка совместима с кодировкой вашей системы по умолчанию (строка, содержащая только символы ASCII в вашей системной кодировке по умолчанию). Но нигде ОП не заявляет об этом.
Фредерик
5
@AMissico Это может привести к тому, что программа даст разные результаты в разных системах . Это никогда не хорошо. Даже если это для создания хэша или чего-то еще (я полагаю, это означает, что OP означает «шифровать»), одна и та же строка всегда должна давать тот же хэш.
Nyerguds
114
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());
Майкл Буэн
источник
2
Вы можете использовать один и тот же экземпляр BinaryFormatter для всех этих операций
Джоэл Коухорн
3
Очень интересно. По-видимому, это исключит любой высокий суррогатный символ Unicode. См. Документацию по [BinaryFormatter ]
95

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

У Джоэла есть пост на эту тему:

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

Жаф - Бен Дугид
источник
6
«1 символ может быть представлен 1 или более байтами» Я согласен. Я просто хочу эти байты независимо от того, в какой кодировке находится строка. Единственный способ сохранить строку в памяти - это байты. Четные символы хранятся как 1 или более байтов. Я просто хочу заполучить их байты.
Агнель Курьян
16
Вам не нужны кодировки, если только вы (или кто-то еще) на самом деле не намерены интерпретировать данные вместо того, чтобы рассматривать их как общий «блок байтов». Для таких вещей, как сжатие, шифрование и т. Д., Беспокоиться о кодировке не имеет смысла. Смотрите мой ответ, чтобы узнать, как это сделать, не беспокоясь о кодировке.
user541686
9
@Mehrdad - В общем, но первоначальный вопрос, как было сказано, когда я первоначально отвечал, не уточнил, что OP будет происходить с этими байтами после того, как они их преобразовали, и для будущих поисковиков информация, которая уместна - это довольно хорошо охвачен ответом Джоэла - и, как вы заявляете в своем ответе: если вы остаетесь в мире .NET и используете свои методы для конвертации в / из, вы счастливы. Как только вы выйдете за пределы этого, кодирование будет иметь значение.
Жаф - Бен Дугид
Одна кодовая точка может быть представлена ​​до 4 байтов. (Одна кодовая единица UTF-32, суррогатная пара UTF-16 или 4 байта UTF-8.) Значения, для которых UTF-8 потребуется более 4 байтов, находятся вне диапазона Юникода 0x0..0x10FFFF. ;-)
DevSolar
89

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

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

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

[For] выражение типа 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, результат зависит от порядкового номера машины. Но автора вопроса это не касается.

Том Блоджет
источник
3
@Jan Это правильно, но длина строки уже дает количество кодовых единиц (не кодовых точек).
Том Блоджет
1
Спасибо что подметил это! Из MSDN: « LengthСвойство [of String] возвращает количество Charобъектов в этом экземпляре, а не количество символов Юникода». Ваш пример кода, следовательно, правильно, как написано.
Ян Хеттих
1
@supercat "Тип char представляет кодовую единицу UTF-16, а строковый тип представляет последовательность кодовых единиц UTF-16." --_ C # 5 Specification._ Хотя, да, нет ничего, что препятствует недопустимой строке Unicode:new String(new []{'\uD800', '\u0030'})
Том Блоджет
1
@TomBlodget: Интересно, что если брать экземпляры Globalization.SortKey, извлекать KeyDataи упаковывать результирующие байты из каждого в String[два байта на символ, сначала MSB ], вызов String.CompareOrdinalрезультирующих строк будет существенно быстрее, чем вызов SortKey.Compareэкземпляров SortKey, или даже призывая memcmpк этим случаям. Учитывая это, мне интересно, почему KeyDataвозвращается, Byte[]а не String?
суперкат
1
Увы, правильный ответ, но слишком поздно, никогда не будет иметь столько голосов, сколько принято. Благодаря TL; DR люди будут думать, что принятый ответ ошеломляет. скопируйте и проголосуйте.
Мартин Каподичи
46

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

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

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

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

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

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

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

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

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

Джоэл Коухорн
источник
9
В .NET есть области, где вам нужно получить байтовые массивы для строк. Многие из классов .NET Cryptrography содержат такие методы, как ComputeHash (), которые принимают байтовый массив или поток. У вас нет другого выбора, кроме как сначала преобразовать строку в байтовый массив (выбирая кодировку), а затем, при желании, обернуть ее в поток. Однако до тех пор, пока вы выбираете кодировку (то есть UTF8), придерживайтесь ее, с этим проблем не возникает.
Эш
44

Просто чтобы показать , что звук Mehrdrad в ответ работает, его подход может даже сохраняться непарные символы суррогатных (многие из которых были направлены против моего ответа, но о которых все одинаково виновны, например 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

谢谢!

Майкл Буэн
источник
3
Разве суррогаты не должны появляться парами, чтобы сформировать правильные кодовые точки? Если это так, я могу понять, почему данные будут искажены.
dtanders
1
@dtanders Да, это тоже мои мысли, они должны появляться в парах, непарные суррогатные символы просто случаются, если вы намеренно ставите их в строку и делаете их непарными. Чего я не знаю, так это того, почему другие разработчики продолжают настаивать на том, что вместо этого мы должны использовать подход, учитывающий кодирование, поскольку они считают, что подход сериализации ( мой ответ , который был принят более 3 лет) не оставляет непарных суррогатный персонаж нетронут. Но они забыли проверить, что их решения, поддерживающие кодирование, не сохраняют непарный суррогатный характер, ирония судьбы ツ
Майкл Буэн,
Если есть библиотека сериализации, которая использует System.Buffer.BlockCopyвнутренне, все аргументы сторонников кодирования будут спорными
Майкл Буэн
2
@MichaelBuen Мне кажется, что главная проблема в том, что вы пишете большими жирными буквами, говоря, что что-то не имеет значения, а не говорите, что это не имеет значения в их случае. В результате вы поощряете людей, которые смотрят на ваш ответ, совершать базовые ошибки программирования, которые в будущем вызовут разочарование других. Непарные суррогаты недопустимы в строке. Это не массив символов, поэтому имеет смысл, что преобразование строки в другой формат приведет к ошибке FFFDв этом символе. Если вы хотите выполнять ручные манипуляции со строками, используйте char [] в соответствии с рекомендациями.
Trisped
2
@dtanders: A System.Stringявляется неизменной последовательностью Char; .NET всегда позволял Stringконструировать объект из любого Char[]и экспортировать его содержимое в одно и Char[]то же значение, даже если оригинал Char[]содержит непарные суррогаты.
суперкат
41

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

System.Text.Encoding.UTF8.GetBytes("TEST String");
Натан
источник
Тогда попробуй это 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
9
@ mg30rg: Почему вы думаете, что ваш пример странный? Конечно, в кодировке с переменной шириной не все символы имеют одинаковые байтовые длины. Что с этим не так?
Влад
@Vlad Более правильный комментарий, однако, заключается в том, что в качестве закодированных символов Юникода (например, байтов) символы, которые содержат свои собственные диакритические знаки, дадут другой результат, чем диакритические знаки, разделенные на символы-модификаторы, добавленные к символу. Но в iirc в .net есть методы, специально предназначенные для их разделения, чтобы получить согласованное представление байтов.
Нергудс
25

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

Это плохо, когда строка, например, исходит от 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);
}
Gman
источник
Вместо того, чтобы использовать ваш собственный метод для преобразования байтового массива в base64, все, что вам нужно было сделать, это использовать встроенный конвертер: Convert.ToBase64String (arr);
Макотосан
@Makotosan спасибо, но я использовал 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).
Ган
23

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

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

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

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

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

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

Konamiman
источник
2
Позвольте мне уточнить: кодировка использовалась для перевода «привет мира» в физические байты. Поскольку строка хранится на моем компьютере, я уверен, что она должна храниться в байтах. Я просто хочу получить доступ к этим байтам, чтобы сохранить их на диске или по любой другой причине. Я не хочу интерпретировать эти байты. Поскольку я не хочу интерпретировать эти байты, необходимость в кодировке на этом этапе столь же неуместна, как и необходимость в телефонной линии для вызова printf.
Агнель Курьян
3
Но опять же, нет концепции преобразования текста в физические байты, если только вы не используете кодировку. Конечно, компилятор каким-то образом хранит строки в памяти - но он просто использует внутреннюю кодировку, которую вы (или кто-либо, кроме разработчика компилятора) не знаете. Итак, что бы вы ни делали, вам нужна кодировка для получения физических байтов из строки.
Konamiman
@Annel Kurian: Конечно, у строки есть куча байтов, в которых хранится ее содержимое (UTF-16 в воздухе). Но есть веская причина помешать вам получить к нему доступ: строки неизменны, и если вы можете получить внутренний массив byte [], вы также можете изменить его. Это нарушает неизменность, что крайне важно, поскольку несколько строк могут совместно использовать одни и те же данные. Использование кодировки UTF-16 для получения строки, вероятно, просто скопирует данные.
ollb
2
@ Gnafoo, копия байтов подойдет.
Агнель Курьян
22

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

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
Шьям сундар шах
источник
17
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
gkrogers
источник
Но почему следует учитывать кодировку? Почему я не могу просто получить байты, не видя, какая кодировка используется? Даже если бы это было необходимо, разве сам объект String не должен знать, какая кодировка используется, и просто выгружать то, что находится в памяти?
Агнель Курьян
5
Это не всегда работает. Некоторые специальные символы могут быть потеряны при использовании такого метода, который я нашел трудным путем.
JB King
17

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

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);
Джарвис Старк
источник
VUP этот решил мою проблему (byte [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd
16

С появлением 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>требует установки пакета System.Memory NuGet .

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

Джон Раш
источник
13

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

возьмите пример этого примера:

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, но это будет неэффективно с пространством хранения.

Эд Марти
источник
10

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

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

Ганс Пассант
источник
3
UTF-8 компактен, только если большинство ваших символов в наборе символов английского языка (ASCII). Если бы у вас была длинная строка китайских символов, UTF-16 была бы более компактной кодировкой, чем UTF-8 для этой строки. Это связано с тем, что UTF-8 использует один байт для кодирования ASCII и 3 (или, возможно, 4) в противном случае.
Джоэл Мюллер
7
Правда. Но как вы можете не знать о кодировании, если вы знакомы с обработкой китайского текста?
Ганс Пассант
9

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

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

Результат:

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
машет
источник
ОП специально просит НЕ указывать кодировку ... "без указания конкретной кодировки вручную"
Ferdz
8

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

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

РЕДАКТИРОВАТЬ, как прокомментировал Макотосан, теперь это лучший способ:

Encoding.UTF8.GetBytes(text)
Алессандро Аннини
источник
8
ASCIIEncoding ..... не требуется. Простое использование Encoding.UTF8.GetBytes (текст) является предпочтительным.
Макотосан
8

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

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

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

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

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

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

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

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

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

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

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

Джейсон Гомаат
источник
На самом деле строка в C # НЕ ограничена только UTF-16. Что верно, так это то, что он содержит вектор из 16-битных кодовых единиц, но эти 16-битные кодовые единицы не ограничены действительным UTF-16. Но так как они 16-битные, вам нужна кодировка (порядок байтов), чтобы преобразовать их в 8-битные. Строка может хранить данные не в Юникоде, включая двоичный код (например, растровое изображение). Он интерпретируется как UTF-16 только в устройствах ввода-вывода и форматирования текста, которые делают такую ​​интерпретацию.
verdy_p
Таким образом, в строке C # вы можете безопасно хранить кодовую единицу, например 0xFFFF или 0xFFFE, даже если они не являются символами в UTF-16, и вы можете хранить изолированную 0xD800, за которой не следует единица кода, в 0xDC00..0xDFFF (т.е. непарные суррогаты, которые недействительны в UTF-16). Это же замечание относится к строкам в Javascript / ECMAscript и Java.
verdy_p
Когда вы используете «GetBytes», конечно, вы не указываете кодировку, но вы принимаете порядок байтов, чтобы получить два байта в спецификации для каждой единицы кода, хранящейся локально в строке. Когда вы строите новую строку из байтов, вам также нужен конвертер, не обязательно UTF-8 в UTF-16, вы можете вставить дополнительный 0 в старший байт или упаковать два байта (в MSB первый или LSB первый порядок) в тот же 16-битный кодовый блок. Строки имеют компактную форму для массивов 16-битных целых чисел. Связь с «символами» является еще одной проблемой, в C # они не являются реальными типами, поскольку они все еще представлены в виде строк
verdy_p
7

Наиболее близким подходом к вопросу ОП является вопрос Тома Блоджетта, который фактически входит в объект и извлекает байты. Я говорю ближе всего, потому что это зависит от реализации объекта String.

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

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

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

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

"Why is there a dependency on character encodings?"

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

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

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

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


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

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

Жерар ОНЕЙЛ
источник
Эти символы не поддерживаются UTF-8 или UTF-16 или даже UTF-32, например: 񩱠& (Char) 55906& (Char) 55655. Так что вы можете ошибаться, и ответ Мехрдада - это безопасное преобразование без учета того, какой тип кодирования используется.
Мойтаба Резаян
Raymon, символы уже представлены некоторым значением Юникода - и все значения Юникода могут быть представлены всеми UTF. Есть более длинное объяснение того, о чем ты говоришь? В какой кодировке существуют эти два значения (или 3 ..)?
Джерард ONeill
Это недопустимые символы, которые не поддерживаются никакими диапазонами кодирования. Это не значит, что они на 100% бесполезны. Код, который преобразует любой тип строки в эквивалент байтового массива независимо от кодировок, вовсе не является неправильным решением и в некоторых случаях имеет свои собственные применения.
Мойтаба Резаян
1
Хорошо, тогда я думаю, что вы не понимаете проблему. Мы знаем, что это Unicode-совместимый массив - фактически, потому что это .net, мы знаем, что это UTF-16. Так что этих персонажей там не будет. Вы также не полностью прочитали мой комментарий об изменении внутренних представлений. String - это объект, а не кодированный байтовый массив. Поэтому я не согласен с вашим последним утверждением. Вы хотите, чтобы код преобразовывал все строки Unicode в любую кодировку UTF. Это делает то, что вы хотите, правильно.
Джерард ONeill
Объекты представляют собой последовательность данных, первоначально последовательность битов, которые описывают объект в его текущем состоянии. Таким образом, все данные в языках программирования могут быть преобразованы в массив байтов (каждый байт определяет 8 бит), так как вам может потребоваться сохранить некоторое состояние любого объекта в памяти. Вы можете сохранить и сохранить последовательность байтов в файле или памяти и преобразовать ее как целое число, bigint, изображение, строку Ascii, строку UTF-8, зашифрованную строку или свой собственный определенный тип данных после чтения его с диска. Таким образом, вы не можете сказать, что объекты являются чем-то отличным от последовательности байтов.
Мойтаба Резаян
6

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

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
Шьям сундар шах
источник
3

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

[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()

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

System.Text.Encoding.Unicode.GetString()
Jodrell
источник
3

Вот моя небезопасная реализация 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;
}

Это намного быстрее, чем принятый ответ, даже если не так элегантно, как есть. Вот мои тесты секундомера за 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
    }
}
Томмазо Беллуццо
источник
Является ли значение, RuntimeHelpers.OffsetToStringDataкратное 8, в версиях .NET для Itanium? Потому что в противном случае это не удастся из-за невыровненных чтений.
Джон Ханна
не будет ли проще вызвать memcpy? stackoverflow.com/a/27124232/659190
Джодрелл
2

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

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
Алиреза Амини
источник
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
2

Строка может быть преобразована в байтовый массив несколькими различными способами из-за следующего факта: .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

Это зависит от того, что вы хотите байты для

Это потому, что, как метко сказал Тайлер : «Строки не являются чистыми данными. Они также имеют информацию ». В этом случае информация представляет собой кодировку, которая была принята при создании строки.

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

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

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

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

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

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

NH.
источник
2

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

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

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

  • Возможно, вам придется общаться с процессом, реализованным на другом языке или во время выполнения. (Например, это может быть сервер, работающий на другом компьютере или отправляющий строку клиенту 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")

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

jpmc26
источник
1

Два пути:

public static byte[] StrToByteArray(this string s)
{
    List<byte> value = new List<byte>();
    foreach (char c in s.ToCharArray())
        value.Add(c.ToByte());
    return value.ToArray();
}

А также,

public static byte[] StrToByteArray(this string s)
{
    s = s.Replace(" ", string.Empty);
    byte[] buffer = new byte[s.Length / 2];
    for (int i = 0; i < s.Length; i += 2)
        buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
    return buffer;
}

Я склонен использовать нижнюю чаще, чем верхнюю, не оценивая их по скорости.


источник
4
А как насчет многобайтовых символов?
Агнель Курьян
c.ToByte () является приватным: S
Ходор
@AgnelKurian Msdn говорит: «Этот метод возвращает беззнаковое значение байта, которое представляет числовой код объекта Char, переданного ему. В .NET Framework объект Char является 16-разрядным значением. Это означает, что метод подходит для возврата числовые коды символов в диапазоне символов ASCII или в элементах управления Unicode C0 и базовой латинице, а также в диапазонах управления C1 и Latin-1, от U + 0000 до U + 00FF. "
mg30rg
1
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
user1120193
источник