net - c# string default encoding




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

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

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

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

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

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

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

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

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

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


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

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

Ответ Mehrdad's

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

Ответ Tom Blodget

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

joelonsoftware.com/articles/Unicode.html


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

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

Результат:

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

На первую часть вашего вопроса (как получить байты) уже ответили другие: посмотрите в пространстве имен 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");

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

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


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


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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

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

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

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


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

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

byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}




character-encoding