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



default encoding (25)

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

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

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


Answers

Ближайшим подходом к вопросу 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, потому что он взял вас к направлению «байтов строкового объекта». Однако это зависит от реализации, и потому, что он заглядывает внутрь, может быть сложно восстановить копию строки.

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

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

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


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


Чтобы продемонстрировать, что звуковой share Меддрада работает, его подход может даже сохраняться в [BinaryFormatter (многие из которых выровнялись против моего ответа, но каждый из них одинаково виновен, например 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 или сторонний компонент), который может сохранять строки даже в том случае, если содержит непарные суррогатные символы; Я google это время от времени: сериализация непарный суррогатный символ .NET . Это не заставляет меня потерять сон, но это раздражает, когда время от времени кто-то комментирует мой ответ, что он испорчен, но их ответы одинаково ошибочны, когда речь идет о непарных суррогатных персонажах.

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

谢谢!


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

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

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


Два пути:

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

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


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

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

joelonsoftware.com/articles/Unicode.html


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


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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

Кстати, я настоятельно рекомендую вам (и всем, если на то пошло) прочитать эту небольшую часть мудрости: joelonsoftware.com/articles/Unicode.html


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

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

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

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

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

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

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

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

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


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

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

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

Плохо, когда строка, например, поступает из 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);
}

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

Вот моя небезопасная реализация 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's, даже если не такой элегантный, как есть. Вот мои тесты секундомера более 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
    }
}

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

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

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

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

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

Encoding.UTF8.GetBytes(text)

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

Кроме того, .NET поддерживает кодировки, отличные от 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? :-)

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

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

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

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

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

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

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

Результат:

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

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

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

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

Это для большинства обычаев. Если это то, что вам нужно, перестаньте читать здесь. См. Статью забавы joelonsoftware.com/articles/Unicode.html если вы не понимаете, что такое кодировка.

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

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

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

Из спецификации 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 .

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

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

Для этого компилятор пишет код, пропускающий другие части строкового объекта с помощью RuntimeHelpers.OffsetToStringData . Итак, чтобы получить необработанные байты, просто создайте указатель на строку и скопируйте необходимое количество байтов.

// using System.Runtime.InteropServices
unsafe byte[] GetRawBytes(String s)
{
    if (s == null) return null;
    var codeunitCount = s.Length;
    /* We know that String is a sequence of UTF-16 codeunits 
       and such codeunits are 2 bytes */
    var byteCount = codeunitCount * 2; 
    var bytes = new byte[byteCount];
    fixed(void* pRaw = s)
    {
        Marshal.Copy((IntPtr)pRaw, bytes, 0, byteCount);
    }
    return bytes;
}

Как отметил @CodesInChaos, результат зависит от точности машины. Но автор вопроса не связан с этим.


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

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

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

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

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

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

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

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

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

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


Наконец, некоторые предлагают обернуть список в чем-то:

Это правильный путь. «Безусловно многословие» - это плохой способ взглянуть на это. Он имеет явное значение, когда вы пишете my_team.Players.Count . Вы хотите подсчитать игроков.

my_team.Count

.. ничего не значит. Считаете что?

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

Если вы действительно обеспокоены тем, что это слишком многословный, вы всегда можете выставлять свойства из команды:

public int PlayerCount {
    get {
        return Players.Count;
    }
}

.., который становится:

my_team.PlayerCount

Теперь это имеет смысл и придерживается Закона Деметры .

Вы также должны рассмотреть возможность соблюдения принципа повторного использования композитов . Наследуя List<T> , вы говорите, что команда - это список игроков и выставляя из нее ненужные методы. Это неверно. Как вы сказали, команда - это больше, чем список игроков: у нее есть имя, менеджеры, члены совета, тренеры, медицинский персонал, зарплата и т. Д. Если ваш класс команды содержит список игроков, вы «У команды есть список игроков», но у нее могут быть и другие вещи.





c# .net string character-encoding