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

2003

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

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

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

  • 0
    Ваше замешательство по поводу роли кодирования заставляет меня задуматься, если это правильный вопрос. Почему вы пытаетесь преобразовать строку в байтовый массив? Что вы собираетесь делать с байтовым массивом?
  • 0
    Я собираюсь зашифровать это. Я могу зашифровать его без конвертации, но я все же хотел бы знать, почему здесь используется кодирование. Просто дай мне байты, это то, что я говорю.
Показать ещё 34 комментария
Теги:
string
character-encoding

38 ответов

1853
Лучший ответ

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

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

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

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

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

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

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

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

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

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

  • 5
    +1 Точно мои мысли, я не знаю настойчивости некоторых заглядываний здесь о кодировании. Просто нужно сделать дамп памяти / сериализацию (хотя библиотека по умолчанию для сериализации от Microsoft имеет недостатки). Я надеюсь, что знаю этот BlockCopy API раньше :-)
  • 3
    @MichaelBuen: Да. Пока ваши дампы памяти / сериализации не пытаются интерпретировать данные, все в порядке. GetBytes запомнить следующее правило: если вашей программе (или другой программе) необходимо преобразовать выходные данные GetBytes обратно в одну и ту же строку, она может использовать для этого только GetString . Пока вы не нарушаете это, вы можете полностью игнорировать концепцию кодирования.
Показать ещё 122 комментария
1119

Это зависит от кодировки вашей строки (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 использует UTF-16 для представления строк, поэтому, если вы просто хотите получить точные байты, которые использует .NET, используйте System.Text.Encoding.Unicode.GetBytes (...).

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

  • 14
    Но почему следует учитывать кодировку? Почему я не могу просто получить байты, не видя, какая кодировка используется? Даже если бы это было необходимо, разве сам объект String не должен знать, какая кодировка используется, и просто выгружать то, что находится в памяти?
  • 53
    Строки .NET всегда кодируются как Unicode. Так что используйте System.Text.Encoding.Unicode.GetBytes (); чтобы получить набор байтов, который .NET использовал бы для представления символов. Однако зачем вам это? Я рекомендую UTF-8, особенно когда большинство символов в западном латинском наборе.
Показать ещё 7 комментариев
247

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

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

  • 85
    Принятый ответ не только очень сложный, но и рецепт катастрофы.
  • 12
    В случае, если принятый ответ будет изменен, для целей записи, это ответ Mehrdad в это время и дату. Надеемся, что ОП еще раз займется этим и примет лучшее решение.
Показать ещё 9 комментариев
112
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 ]
Показать ещё 1 комментарий
88

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

У Джоэля есть проводка по этому поводу:

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

  • 5
    «1 символ может быть представлен 1 или более байтами» Я согласен. Я просто хочу эти байты независимо от того, в какой кодировке находится строка. Единственный способ сохранить строку в памяти - это байты. Четные символы хранятся как 1 или более байтов. Я просто хочу заполучить их байты.
  • 15
    Вам не нужны кодировки, если только вы (или кто-то еще) на самом деле не намерены интерпретировать данные вместо того, чтобы рассматривать их как общий «блок байтов». Для таких вещей, как сжатие, шифрование и т. Д., Беспокоиться о кодировке не имеет смысла. Смотрите мой ответ, чтобы узнать, как это сделать, не беспокоясь о кодировке.
Показать ещё 2 комментария
79

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

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

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

Что это для большинства обычаев. Если это вам нужно, перестаньте читать здесь. См. Статью статья Джоэл Спольски, если вы не понимаете, что такое кодировка.

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

Теперь автор вопроса спрашивает: "Каждая строка хранится как массив байтов, правильно? Почему я не могу просто иметь эти байты?"

Он не хочет конверсии.

Из С# spec:

Обработка символов и строк в С# использует кодировку Unicode. charтип представляет собой кодовый блок UTF-16, а тип строки представляет собой последовательность кодовых блоков UTF-16.

Итак, мы знаем, что если мы попросим нулевое преобразование (т.е. от UTF-16 до UTF-16), мы получим желаемый результат:

Encoding.Unicode.GetBytes(".NET String to byte array")

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

".NET String to byte array".ToCharArray()

Это не дает нам желаемого типа данных, но Ответ Mehrdad показывает, как преобразовать этот массив Char в массив байтов, используя BlockCopy. Однако это копирует строку дважды! И он слишком явно использует кодирующий код: тип данных System.Char.

Единственный способ получить фактические байты, в которых хранится String, - это использовать указатель. Оператор fixed позволяет принимать адрес значений. Из спецификации С#:

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

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

  • 0
    В общем случае некорректно устанавливать byteCount в два раза больше длины строки. Для кодовых точек Unicode вне Базовой многоязычной плоскости для каждого символа будут использоваться две 16-битные кодовые единицы.
  • 2
    @Jan Это правильно, но длина строки уже дает количество кодовых единиц (не кодовых точек).
Показать ещё 13 комментариев
40

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

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

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

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

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

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

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

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

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

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

Просто чтобы продемонстрировать, что звуковой ответ 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
    Разве суррогаты не должны появляться в парах, чтобы сформировать правильные кодовые точки? Если это так, я могу понять, почему данные будут искажены.
  • 1
    @dtanders Да, это тоже мои мысли, они должны появляться в парах, непарные суррогатные символы просто случаются, если вы намеренно ставите их в строку и делаете их непарными. Чего я не знаю, так это того, почему другие разработчики продолжают настаивать на том, что вместо этого мы должны использовать подход, учитывающий кодирование, поскольку они считают, что подход сериализации ( мой ответ , который был принят более 3 лет) не оставляет непарных суррогатный персонаж нетронут. Но они забыли проверить, что их решения, поддерживающие кодирование, не сохраняют непарный суррогатный характер, ирония судьбы ツ
Показать ещё 8 комментариев
36

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

System.Text.Encoding.UTF8.GetBytes("TEST String");
  • 0
    Затем попробуйте этот 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
  • 3
    @ mg30rg: Как вы думаете, почему ваш пример странный? Конечно, в кодировке с переменной шириной не все символы имеют одинаковые байтовые длины. Что с этим не так?
22

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

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

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

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);
}
  • 0
    Вместо того, чтобы использовать ваш собственный метод для преобразования байтового массива в base64, все, что вам нужно было сделать, это использовать встроенный конвертер: Convert.ToBase64String (arr);
  • 0
    @Makotosan спасибо, но я использовал Convert.ToBase64String(arr); для byte[] (data) <-> string (serialized data to store in XML file) преобразования base64 byte[] (data) <-> string (serialized data to store in XML file) . Но чтобы получить начальный byte[] (data) мне нужно было что-то сделать со String , содержащей двоичные данные (именно так MSSQL и возвращает их мне). Поэтому приведенные выше функции предназначены для String (binary data) <-> byte[] (easy accessible binary data) .
20

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

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

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

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

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

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

  • 2
    Позвольте мне уточнить: кодировка была использована для перевода «привет» в физические байты. Поскольку строка хранится на моем компьютере, я уверен, что она должна храниться в байтах. Я просто хочу получить доступ к этим байтам, чтобы сохранить их на диске или по любой другой причине. Я не хочу интерпретировать эти байты. Поскольку я не хочу интерпретировать эти байты, необходимость в кодировании на этом этапе столь же неуместна, как и необходимость в телефонной линии для вызова printf.
  • 2
    Но опять же, нет концепции преобразования текста в физические байты, если только вы не используете кодировку. Конечно, компилятор каким-то образом хранит строки в памяти - но он просто использует внутреннюю кодировку, которую вы (или кто-либо, кроме разработчика компилятора) не знаете. Итак, что бы вы ни делали, вам нужна кодировка для получения физических байтов из строки.
Показать ещё 2 комментария
19

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

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
15

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

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);
  • 0
    VUP это один решил мою проблему (byte [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
15
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
  • 0
    Но почему следует учитывать кодировку? Почему я не могу просто получить байты, не видя, какая кодировка используется? Даже если бы это было необходимо, разве сам объект String не должен знать, какая кодировка используется, и просто выгружать то, что находится в памяти?
  • 5
    Это не всегда работает. Некоторые специальные символы могут быть потеряны при использовании такого метода, который я нашел трудным путем.
Показать ещё 1 комментарий
11

Я не уверен, но я думаю, что строка сохраняет свою информацию как массив Chars, который неэффективен с байтами. В частности, определение 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 имеет множество способов сопоставления строки с байтом [], вам нужно выбрать ту, которая позволяет избежать потери информации, и которая проста в использовании вашим клиентом, когда ей нужно сопоставить байт [] назад к строке.

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

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

С появлением Span<T> выпущенного в С# 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 должны NonPortableCast аргумент, что вы, вероятно, не должны этого делать.

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

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

7

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

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

Результат:

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

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

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

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

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

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

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

Mehrdad Ответ

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

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

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

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

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

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

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

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

5

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

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

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

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

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

"Why is there a dependency on character encodings?"

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

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

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

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


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

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

  • 0
    Raymon, символы уже представлены некоторым значением Unicode - и все значения Unicode могут быть представлены всеми UTF. Есть более длинное объяснение того, о чем ты говоришь? В какой кодировке существуют эти два значения (или 3 ..)?
  • 0
    Это недопустимые символы, которые не поддерживаются никакими диапазонами кодирования. Это не значит, что они на 100% бесполезны. Код, который преобразует любой тип строки в эквивалент байтового массива независимо от кодировок, вовсе не является неправильным решением и в некоторых случаях имеет свои собственные применения.
Показать ещё 3 комментария
5

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

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
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;
}

Это намного быстрее, чем принятый 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 этот метод также можно использовать как расширение строки:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
  • 0
    Является ли значение RuntimeHelpers.OffsetToStringData кратным 8 в версиях .NET для Itanium? Потому что в противном случае это не удастся из-за невыровненных чтений.
  • 0
    не будет ли проще вызвать memcpy ? stackoverflow.com/a/27124232/659190
2

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

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
  • 0
    ... и потерять всех персонажей со скоростью прыжка выше 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." потерять информацию, которую невозможно восстановить. (И я еще не упомянул азиатские языки, где вы потеряете все символы.)
2

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

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

простой код с LINQ

string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();

EDIT: как указано ниже, это не очень хорошо.

но вы все равно можете использовать его для понимания LINQ с более подходящим кодированием:

string s = "abc"
byte[] b = s.Cast<byte>().ToArray();
  • 2
    Это чуть быстрее , не говоря уже о самом быстром . Это, безусловно, интересная альтернатива, но по сути она такая же, как Encoding.Default.GetBytes(s) которая, кстати, намного быстрее . Быстрое тестирование показывает, что Encoding.Default.GetBytes(s) работает как минимум на 79% быстрее. YMMV.
  • 4
    Попробуйте это с . Этот код не завершится с ошибкой , но вернет неправильный результат (что еще хуже). Попробуйте привести к short вместо byte чтобы увидеть разницу.
2
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
2

Два способа:

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
    А как насчет многобайтовых символов?
  • 0
    c.ToByte () является частным: S
Показать ещё 1 комментарий
1

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

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

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

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

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

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

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

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

1

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

Ref эта ссылка.

Для сериализации массива байтов с помощью 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? :-)
0

Я написал расширение Visual Basic, подобное принятому ответу, но напрямую использую память .NET и Marshalling для преобразования, и он поддерживает диапазоны символов, не поддерживаемые другими способами, например UnicodeEncoding.UTF8.GetString или UnicodeEncoding.UTF32.GetString или даже MemoryStream and BinaryFormatter (недопустимые символы: ? и ChrW(55906) и ChrW(55655)):

<Extension> _
Public Function ToBytesMarshal(ByRef str As String) As Byte()
    Dim gch As GCHandle = GCHandle.Alloc(str, GCHandleType.Pinned)
    Dim handle As IntPtr = gch.AddrOfPinnedObject
    ToBytesMarshal = New Byte(str.Length * 2 - 1) {}
    Try
        For i As Integer = 0 To ToBytesMarshal.Length - 1
            ToBytesMarshal.SetValue(Marshal.ReadByte(IntPtr.Add(handle, i)), i)
        Next
    Finally
        gch.Free()
    End Try
End Function

<Extension> _
Public Function ToStringMarshal(ByRef arr As Byte()) As String
    Dim gch As GCHandle = GCHandle.Alloc(arr, GCHandleType.Pinned)
    Try
        ToStringMarshal = Marshal.PtrToStringAuto(gch.AddrOfPinnedObject)
    Finally
        gch.Free()
    End Try
End Function
0

Символ - это ключ поиска в таблице шрифтов и лексические традиции, такие как заказы, версии верхнего и нижнего регистра и т.д.

Следовательно, символ не является байтом (8 бит), а байт не является символом. В частности, 256 перестановок байта не могут разместить тысячи символов на некоторых письменных языках, а тем более на всех языках. Следовательно, были разработаны различные методы кодирования символов. Некоторые кодируют для определенного класса языков (ASCII-кодирование); несколько языков с использованием кодовых страниц (Extended ASCII); или, амбициозно, все языки, выборочно включающие дополнительные байты по мере необходимости, Unicode.

В рамках системы, такой как .NET framework, String подразумевает конкретную кодировку символов. В .NET это кодирование Unicode. Поскольку структура читает и записывает Unicode по умолчанию, обращение к кодировке символов обычно не требуется в .NET.

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

  • 1
    Юникод не является кодировкой. Юникод - это абстрактное сопоставление символов с кодовыми точками. Есть несколько способов кодирования Unicode; в частности, UTF-8 и UTF-16 являются наиболее распространенными. .NET использует UTF-16, хотя я не уверен, что это UTF-16 LE или UTF-16 BE.
-1

От byte[] до string:

        return BitConverter.ToString(bytes);
  • 1
    Это не то, о чем просил ОП ...
-1

Чтобы преобразовать строку в байт [], используйте следующее решение:

string s = "abcdefghijklmnopqrstuvwxyz";
byte[] b = System.Text.UTF32Encoding.GetBytes(s);

Я надеюсь, что это помогает.

  • 0
    это не решение проблемы!
  • 0
    Почему ты так говоришь?
Показать ещё 3 комментария
-3
// C# to convert a string to a byte array.
public static byte[] StrToByteArray(string str)
{
    System.Text.ASCIIEncoding  encoding=new System.Text.ASCIIEncoding();
    return encoding.GetBytes(str);
}


// C# to convert a byte array to a string.
byte [] dBytes = ...
string str;
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
str = enc.GetString(dBytes);
  • 6
    1) Это приведет к потере данных из-за использования ASCII в качестве кодировки. 2) Нет смысла создавать новое ASCIIEncoding - просто используйте свойство Encoding.ASCII.
-5

Вот код:

// Input string.
const string input = "Dot Net Perls";

// Invoke GetBytes method.
// ... You can store this array as a field!
byte[] array = Encoding.ASCII.GetBytes(input);

// Loop through contents of the array.
foreach (byte element in array)
{
    Console.WriteLine("{0} = {1}", element, (char)element);
}
  • 4
    Может не работать, если строка не ASCII.
-7

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

string message = "This is a message.";
byte[] bytes = new byte[message.Length];
for (int i = 0; i < message.Length; i++)
    bytes[i] = (byte)message[i];
  • 3
    Это небезопасно, и вы потеряете исходные данные, если входная строка содержит символы диапазона Юникод.
  • 0
    Это было для проекта последовательной связи, который все равно не мог обрабатывать юникод. Конечно, это был чрезвычайно узкий случай.
-15

Вопрос OP: "Как преобразовать массив string в byte в .NET(С#)?" [Так в оригинале]

Вы можете использовать следующий код:

static byte[] ConvertString (string s) {
    return new byte[0];
}

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

  • 0
    Это не обращение. Это новый байтовый массив. ОП действительно нуждался в указателе и memcpy. Или приведение: byte [] b = (byte []) s ;.
  • 1
    Кроме того, "s" здесь даже не используется. Определенно не решение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню