Преобразование строки в байтовый массив в C #

670

Я конвертирую что-то из VB в C #. Возникла проблема с синтаксисом этого оператора:

if ((searchResult.Properties["user"].Count > 0))
{
    profile.User = System.Text.Encoding.UTF8.GetString(searchResult.Properties["user"][0]);
}

Затем я вижу следующие ошибки:

Аргумент 1: невозможно преобразовать из объекта в byte []

Наилучшее совпадение перегруженного метода для System.Text.Encoding.GetString (byte []) содержит неверные аргументы

Я пытался исправить код, основанный на этом посте, но все равно безуспешно

string User = Encoding.UTF8.GetString("user", 0);

Какие-либо предложения?

nouptime
источник
1
Какой тип searchResult.Properties["user"][0]? Попробуйте byte[]
привести
мшсаем пошел туда, куда я шел. Вы пропустили приведение к (byte[])поисковому результату?
Харрисон
2
Вам нужно выяснить, что это за тип Properties["user"][0]. Если вы уверены , что это массив , то вы можете бросить , как этоprofile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
keyboardP
1
Оказывается, не было никакой необходимости во всей этой суете. Имя пользователя может быть получено без кодировки в конце концов.
nouptime
3
Почему вы не выбрали верный ответ?
Али

Ответы:

1189

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

Например, если байтовый массив был создан следующим образом:

byte[] bytes = Encoding.ASCII.GetBytes(someString);

Вам нужно будет превратить его обратно в строку, например так:

string someString = Encoding.ASCII.GetString(bytes);

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

Тимоти Рэндалл
источник
3
Тимоти, я просмотрел код VB и не могу найти байтовый массив, как вы упомянули.
nouptime
Каков тип свойства Properties в результатах поиска?
Тимоти Рэндалл
Все, что я могу видеть, - это то, что в свойствах есть строка с числом элементов. Я не уверен, что это то, что вы спрашивали меня, хотя.
nouptime
16
@AndiAR попробуйте Encoding.UTF8.GetBytes (somestring)
OzBob
1
Для моей ситуации я обнаружил, что Encoding.Unicode.GetBytes работает (но ASCII нет)
Джефф
106

Прежде всего, добавьте System.Textпространство имен

using System.Text;

Тогда используйте этот код

string input = "some text"; 
byte[] array = Encoding.ASCII.GetBytes(input);

Надеюсь это исправить!

Шридхар
источник
42

Также вы можете использовать метод расширения, чтобы добавить метод к stringтипу, как показано ниже:

static class Helper
{
   public static byte[] ToByteArray(this string str)
   {
      return System.Text.Encoding.ASCII.GetBytes(str);
   }
}

И используйте это как ниже:

string foo = "bla bla";
byte[] result = foo.ToByteArray();
Али
источник
12
Я бы переименовал этот метод, чтобы включить тот факт, что он использует кодировку ASCII. Нечто подобное ToASCIIByteArray. Ненавижу, когда узнаю, что какая-то библиотека, которую я использую, использует ASCII, и я предполагаю, что она использует UTF-8 или что-то более современное.
T Blank
30
var result = System.Text.Encoding.Unicode.GetBytes(text);
Куганрайх Раджендран
источник
3
Это должен быть принятый ответ, так как другие ответы предполагают ASCII, но кодировка - это Unicode (это UTF16) или UTF8.
Авель
26
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);
}
Эран Йогев
источник
Это не удастся для символов, попадающих в диапазон суррогатных пар. GetBytes будет иметь байтовый массив, который пропускает один нормальный символ на суррогатную пару с конца. GetString будет иметь пустые символы в конце. Единственный способ, которым это будет работать, - это если по умолчанию Microsoft использует UTF32 или если символы в диапазоне суррогатных пар не разрешены. Или я что-то не вижу? Правильный способ - это «кодировать» строку в байты.
Джерард ONeill
Правильно, для более широкого диапазона вы можете использовать что-то похожее на решение # Тимоти Рэндалла: using System; используя System.Text; namespace Example {открытый класс Program {public static void Main (string [] args) {string s1 = "Hello World"; string s2 = "שלום עולם"; строка s3 = "你好 , 世界!"; ЕЫпе (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (S1))); ЕЫпе (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (с2))); ЕЫпе (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s3))); }}}
Эран Йогев
17

Почему не следует использовать Encoding.Default ...

Ответ Рэндалла использует Encoding.Default, однако Microsoft выдвигает против него предупреждение :

Разные компьютеры могут использовать разные кодировки по умолчанию, и кодировка по умолчанию может меняться на одном компьютере. Если вы используете кодировку по умолчанию для кодирования и декодирования данных, передаваемых между компьютерами или получаемых в разное время на одном и том же компьютере, это может привести к неправильному переводу этих данных. Кроме того, кодировка, возвращаемая свойством Default, использует наиболее подходящий запасной вариант для сопоставления неподдерживаемых символов с символами, поддерживаемыми кодовой страницей. По этим причинам использование кодировки по умолчанию не рекомендуется. Чтобы обеспечить правильное декодирование закодированных байтов, следует использовать кодировку Unicode, например UTF8Encoding или UnicodeEncoding. Вы также можете использовать протокол более высокого уровня, чтобы гарантировать, что тот же формат используется для кодирования и декодирования.

Чтобы проверить, что такое кодировка по умолчанию, используйте Encoding.Default.WindowsCodePage(1250 в моем случае - и, к сожалению, нет предопределенного класса кодировки CP1250, но объект может быть получен как Encoding.GetEncoding(1250)).

Encoding.ASCII 7bit, так что в моем случае это тоже не работает:

byte[] pass = Encoding.ASCII.GetBytes("šarže");
Console.WriteLine(Encoding.ASCII.GetString(pass)); // ?ar?e

... и почему вместо этого следует использовать кодировку UTF-8 ...

Кодировка по умолчанию вводит в заблуждение: .NET использует UTF-8 повсеместно в качестве реального значения по умолчанию (8-битные кодировки устарели к концу 20-го века, проверьте т.е. Console.OutputEncoding.EncodingName*), поэтому каждая константа, которую вы определяете в коде, кодируется в UTF-8 по умолчанию - так этот следует использовать, если источник данных не находится в другой кодировке.

* В моем случае это UTF-8, что является прямой ложью: chcpиз консоли Windows (cmd) возвращает 852 - и это не должно быть изменено, потому что у локализованных системных команд (например, ping) эта кодовая страница жестко закодирована

Следуя рекомендации Microsoft:

var utf8 = new UTF8Encoding();
byte[] pass = utf8.GetBytes("šarže");
Console.WriteLine(utf8.GetString(pass)); // šarže

Encoding.UTF8 Рекомендовано другими - это экземпляр кодировки UTF-8 UF и может также использоваться напрямую или как

var utf8 = Encoding.UTF8 as UTF8Encoding;

... но это не всегда используется

Кодировка для байтовых массивов должна «просто работать» в Unicode в западных странах, но как только вы перенесете свою программу в некоторые менее поддерживаемые регионы (например, здесь, в Восточной Европе), это настоящий беспорядок: в Чешской Республике по умолчанию Windows использует (в 2020 году!) MS нестандартный 852 (он же Latin-2) для консоли, 1250 как Windows OEM, UTF-8 (65001) как .NET (и другие) новый по умолчанию, и мы должны помнить, что некоторые западные 8-битные ЕС данные все еще в 1252 году, в то время как старый 8-битный западный стандарт для Восточной Европы был ISO-8859-2 (он же Latin-2, но НЕ тот же самый Latin-2, как 852). Использование ASCII означает текст, полный тофу и '?' Вот. Так что до половины 21-го века, пожалуйста, установите UTF-8 явно .

Ян Турош
источник
12

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

using System.Text;
public static class StringExtensions
{
    /// <summary>
    /// Creates a byte array from the string, using the 
    /// System.Text.Encoding.Default encoding unless another is specified.
    /// </summary>
    public static byte[] ToByteArray(this string str, Encoding encoding = Encoding.Default)
    {
        return encoding.GetBytes(str);
    }
}

И используйте это как ниже:

string foo = "bla bla";

// default encoding
byte[] default = foo.ToByteArray();

// custom encoding
byte[] unicode = foo.ToByteArray(Encoding.Unicode);
Дэн Синклер
источник
2
Обратите внимание, что использование Encoding encoding = Encoding.Defaultрезультатов приводит к ошибке времени компиляции:CS1736 Default parameter value for 'encoding' must be a compile-time constant
Дуглас Гаскелл
11

Следующий подход будет работать, только если символы 1 байт. (Unicode по умолчанию не будет работать, так как это 2 байта)

public static byte[] ToByteArray(string value)
{            
    char[] charArr = value.ToCharArray();
    byte[] bytes = new byte[charArr.Length];
    for (int i = 0; i < charArr.Length; i++)
    {
        byte current = Convert.ToByte(charArr[i]);
        bytes[i] = current;
    }

    return bytes;
}

Сохраняя это простым

Мандар Судаме
источник
charи stringUTF-16 по определению.
Том Блоджет
Да, по умолчанию используется UTF-16. Я не делаю никаких предположений о кодировании входной строки.
Мандар Судаме
Там нет текста, но закодированный текст. Ваш ввод является типом stringи, следовательно, UTF-16. UTF-16 не используется по умолчанию; в этом нет выбора. Затем вы разбиваетесь на char[]единицы кода UTF-16. Затем вы вызываете Convert.ToByte (Char) , который просто преобразует U + 0000 в U + 00FF в ISO-8859-1 и корректирует любые другие кодовые точки.
Том Блоджет
Имеет смысл. Благодарю за разъяснение. Обновление моего ответа.
Мандар Судаме
1
Я думаю, что вы все еще упускаете несколько важных моментов. Сосредоточьтесь на том, charчтобы быть 16 битами и Convert.ToByte()выбрасывать половину из них.
Том Блоджет
10

использовать этот

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
Алиреза Амини
источник
6

Уточнение правки Джастина Столя (использование Эраном Йогевым BlockCopy).

Предлагаемое решение действительно быстрее, чем с использованием Encoding. Проблема в том, что он не работает для кодирования байтовых массивов неравной длины. Как дано, это вызывает исключение. Увеличение длины на 1 оставляет завершающий байт при декодировании из строки.

Для меня необходимость возникла, когда я хотел кодировать из DataTableв JSON. Я искал способ кодировать двоичные поля в строки и декодировать из строки обратно в byte[].

Поэтому я создал два класса - один, который упаковывает вышеуказанное решение (при кодировании из строк это нормально, потому что длины всегда четные), а другой - для byte[]кодирования.

Я решил проблему с неравной длиной, добавив один символ, который говорит мне, была ли исходная длина двоичного массива нечетной ('1') или четной ('0')

Следующее:

public static class StringEncoder
{
    static byte[] EncodeToBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
    static string DecodeToString(byte[] bytes)
    {
        char[] chars = new char[bytes.Length / sizeof(char)];
        System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
        return new string(chars);
    }
}

public static class BytesEncoder
{
    public static string EncodeToString(byte[] bytes)
    {
        bool even = (bytes.Length % 2 == 0);
        char[] chars = new char[1 + bytes.Length / sizeof(char) + (even ? 0 : 1)];
        chars[0] = (even ? '0' : '1');
        System.Buffer.BlockCopy(bytes, 0, chars, 2, bytes.Length);

        return new string(chars);
    }
    public static byte[] DecodeToBytes(string str)
    {
        bool even = str[0] == '0';
        byte[] bytes = new byte[(str.Length - 1) * sizeof(char) + (even ? 0 : -1)];
        char[] chars = str.ToCharArray();
        System.Buffer.BlockCopy(chars, 2, bytes, 0, bytes.Length);

        return bytes;
    }
}
user4726577
источник
4

На этот вопрос отвечали достаточно много раз, но с C # 7.2 и введением типа Span, существует более быстрый способ сделать это в небезопасном коде:

public static class StringSupport
{
    private static readonly int _charSize = sizeof(char);

    public static unsafe byte[] GetBytes(string str)
    {
        if (str == null) throw new ArgumentNullException(nameof(str));
        if (str.Length == 0) return new byte[0];

        fixed (char* p = str)
        {
            return new Span<byte>(p, str.Length * _charSize).ToArray();
        }
    }

    public static unsafe string GetString(byte[] bytes)
    {
        if (bytes == null) throw new ArgumentNullException(nameof(bytes));
        if (bytes.Length % _charSize != 0) throw new ArgumentException($"Invalid {nameof(bytes)} length");
        if (bytes.Length == 0) return string.Empty;

        fixed (byte* p = bytes)
        {
            return new string(new Span<char>(p, bytes.Length / _charSize));
        }
    }
}

Имейте в виду, что байты представляют строку в кодировке UTF-16 (называемую «Unicode» в C # land).

Некоторый быстрый сравнительный анализ показывает, что вышеупомянутые методы примерно в 5 раз быстрее, чем их реализации Encoding.Unicode.GetBytes (...) / GetString (...) для строк среднего размера (30-50 символов), и даже быстрее для больших строк. Эти методы также работают быстрее, чем использование указателей с Marshal.Copy (..) или Buffer.MemoryCopy (...).

Algemist
источник
4

Если результатом 'searchResult.Properties ["user"] [0] "является строка:

if ( ( searchResult.Properties [ "user" ].Count > 0 ) ) {

   profile.User = System.Text.Encoding.UTF8.GetString ( searchResult.Properties [ "user" ] [ 0 ].ToCharArray ().Select ( character => ( byte ) character ).ToArray () );

}

Ключевым моментом является то, что преобразование строки в byte [] может быть сделано с помощью LINQ:

.ToCharArray ().Select ( character => ( byte ) character ).ToArray () )

И обратное:

.Select ( character => ( char ) character ).ToArray () )
Янус
источник
3

Кто-нибудь видит причину, почему бы не сделать это?

mystring.Select(Convert.ToByte).ToArray()
Lomithrani
источник
10
Convert.ToByte(char)не работает так, как вы думаете. Символ '2'преобразуется в байт 2, а не в байт, представляющий символ '2'. Используйте mystring.Select(x => (byte)x).ToArray()вместо этого.
Джек,
3

Это то, что работает для меня

byte[] bytes = Convert.FromBase64String(textString);
Мина Матта
источник
это работает только тогда, когда ваша строка содержит только az, AZ, 0-9, +, /. Никакие другие символы не допускаются de.wikipedia.org/wiki/Base64
Blechdose
2

Вы можете использовать MemoryMarshal API для очень быстрого и эффективного преобразования. Stringнеявно приводиться к ReadOnlySpan<byte>, так как MemoryMarshal.Castпринимает либо Span<byte>или в ReadOnlySpan<byte>качестве входного параметра.

public static class StringExtensions
{
    public static byte[] ToByteArray(this string s) => s.ToByteSpan().ToArray(); //  heap allocation, use only when you cannot operate on spans
    public static ReadOnlySpan<byte> ToByteSpan(this string s) => MemoryMarshal.Cast<char, byte>(s);
}

Следующий бенчмарк показывает разницу:

Input: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"

|                       Method |       Mean |     Error |    StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------------------------- |-----------:|----------:|----------:|-------:|------:|------:|----------:|
| UsingEncodingUnicodeGetBytes | 160.042 ns | 3.2864 ns | 6.4099 ns | 0.0780 |     - |     - |     328 B |
| UsingMemoryMarshalAndToArray |  31.977 ns | 0.7177 ns | 1.5753 ns | 0.0781 |     - |     - |     328 B |
|           UsingMemoryMarshal |   1.027 ns | 0.0565 ns | 0.1630 ns |      - |     - |     - |         - |
Павел Мага
источник
0

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

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[]))
{
    return s.ToArray();
}
user10863293
источник