it-swarm-ru.tech

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

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

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

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

2059
Agnel Kurian

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

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

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

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

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

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

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

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

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

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

1795
Mehrdad

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

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

1081
bmotmans

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

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

269
Erik A. Brandstadmoen
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());
111
Michael Buen

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

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

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

90
Zhaph - Ben Duguid

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

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

Каждая строка имеет набор символов и кодировку. Когда вы конвертируете объект System.String в массив System.Byte, у вас все еще есть набор символов и кодировка. В большинстве случаев вы знаете, какой набор символов и кодировку вам нужны, а .NET упрощает "копирование с преобразованием". Просто выберите подходящий Encoding класс.

// using System.Text;
Encoding.UTF8.GetBytes(".NET String to byte array")

Преобразование может потребоваться для обработки случаев, когда целевой набор символов или кодировка не поддерживает символ, который находится в источнике. У вас есть несколько вариантов: исключение, замена или пропуск. Политика по умолчанию заменяет "?".

// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100")); 
                                                      // -> "You win ?100"

Очевидно, что конверсии не обязательно без потерь!

Примечание. Для System.String исходным набором символов является Unicode.

Единственная путаница в том, что .NET использует имя набора символов для имени одной конкретной кодировки этого набора символов. Encoding.Unicode должен называться Encoding.UTF16.

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

Конкретная потребность

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

Он не хочет никакого обращения.

Из C # spec :

Обработка символов и строк в C # использует кодировку Unicode. Тип 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, результат зависит от порядкового номера машины. Но автора вопроса это не касается.

85
Tom Blodget

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

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

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

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

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

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

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

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

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

43
Joel Coehoorn

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

谢谢!

43
Michael Buen

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

System.Text.Encoding.UTF8.GetBytes("TEST String");
39
Nathan

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

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

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

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

    return data;
}

public static string StringFromBytes(byte[] arr)
{
    char[] ch = new char[arr.Length / 2];
    for (int i = 0; i < ch.Length; ++i)
    {
        ch[i] = (char)((int)arr[i * 2] + (((int)arr[i * 2 + 1]) << 8));
    }
    return new String(ch);
}
25
Gman

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

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

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

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

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

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

22
Konamiman

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

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

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

string s = "Hello World";

// String to Byte[]

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

// OR

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

// Byte[] to string

string str = System.Text.Encoding.UTF8.GetString(byte1);
17
Jarvis Stark
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
16
gkrogers

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

13
Ed Marty

С появлением 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 вместо рассуждения о данных как о строках вообще.

12
John Rasch

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

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

11
Hans Passant

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

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

Результат:

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
8
mashet

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

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

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

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

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

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

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

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

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

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

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

8
Jason Goemaat

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

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

EDIT как прокомментировал Макотосан, теперь это лучший способ:

Encoding.UTF8.GetBytes(text)
8
Sunrising

Наиболее близким подходом к вопросу ОП является вопрос Тома Блоджетта, который фактически входит в объект и извлекает байты. Я говорю ближе всего, потому что это зависит от реализации объекта 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 #). Однако что это за представление, не имеет значения, что технически является ответом на ФП. Так что - если вы все равно собираетесь конвертировать ... Почему бы не "кодировать"?

6
Gerard ONeill

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

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
6
Shyam sundar shah

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

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

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

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

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
3
alireza amini

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

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

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

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

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

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

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

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

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

2
NH.

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

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

Правка: как прокомментировано ниже, это не очень хороший способ.

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

string s = "abc"
byte[] b = s.Cast<byte>().ToArray();
2
Avlin

Два пути:

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

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

2
harmonik
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
user1120193

Символ является одновременно ключом поиска в таблице шрифтов и лексической традицией, такой как упорядочение, прописные и строчные буквы и т.д.

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

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

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

0
George

От byte[] до string:

        return BitConverter.ToString(bytes);
0
Piero Alberto

Я написал расширение 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
Mojtaba Rezaeian