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


670

Я щось перетворюю з VB в C #. Проблема з синтаксисом цього твердження:

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

Потім я бачу такі помилки:

Аргумент 1: не вдається перетворити з "об'єкта" в "байт []"

Найкращий збіг методу перевантаження для 'System.Text.Encoding.GetString (байт [])' має деякі недійсні аргументи

Я спробував виправити код на основі цієї публікації, але успіху все ще не було

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

Будь-які пропозиції?


1
Який тип searchResult.Properties["user"][0]? Спробуйте віддати це byte[]спочатку
mshsayem

mshsayem пішов куди я йшов. Чи не вистачає анонсу для пошуку в (byte[])результатах пошуку?
Гаррісон

2
Потрібно з’ясувати, що це за тип Properties["user"][0]. Якщо ви впевнені, що це байтовий масив, тоді ви можете керувати таким чиномprofile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
клавіатураP

1
Виявляється, не було потреби в усьому метушні. Ім’я користувача можна отримати без кодування.
понеділок

3
Чому ви не обираєте правдиву відповідь?
Алі

Відповіді:


1189

Якщо у вас вже є байтовий масив, вам знадобиться знати, який тип кодування використовувався для перетворення його в цей байтовий масив.

Наприклад, якщо байтовий масив створено так:

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

Вам потрібно буде повернути його в такий рядок:

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

Якщо ви можете знайти в спадковому коді кодування, яке використовується для створення байтового масиву, тоді вам слід встановити.


3
Тимофі, я переглянув код VB і, здається, не можу знайти байтовий масив, як ви згадали.
понеділок

Який тип властивості Properties у вашому результаті пошуку?
Тімоті Рандалл

Все, що я бачу, - це те, що до Властивості у вигляді рядка є ряд елементів. Я не впевнений, чи це ти просив мене.
понеділок

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

Правильно, для ширшого діапазону ви можете використовувати щось подібне до рішення # Тимоті Рандалл: використання системи; за допомогою System.Text; Приклад простору імен {Програма публічного класу {public static void Main (string [] args) {string s1 = "Hello World"; рядок s2 = "שלום עולם"; рядок s3 = "你好 , 世界!"; Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s1))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s2))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s3))); }}}
Еран Йогев

17

Чому Encoding.Default не слід використовувати ...

@ Рандалл використовує відповідь Encoding.Default, однак Microsoft попереджає :

Різні комп’ютери можуть використовувати різні кодування як типові, а кодування за замовчуванням можуть змінюватися на одному комп’ютері. Якщо ви використовуєте кодування за замовчуванням для кодування та декодування даних, що передаються між комп'ютерами або отримані в різний час на одному комп’ютері, вони можуть перевести ці дані неправильно. Крім того, кодування, повернене властивістю за замовчуванням, використовує найкраще підходить резервне копіювання для відображення непідтримуваних символів на символи, підтримувані кодовою сторінкою. З цих причин використовувати кодування за замовчуванням не рекомендується. Щоб переконатися, що закодовані байти правильно розшифровані, слід використовувати кодування Unicode, наприклад UTF8Encoding або UnicodeEncoding. Ви також можете використовувати протокол вищого рівня, щоб переконатися, що той самий формат використовується для кодування та декодування.

Щоб перевірити, що таке кодування за замовчуванням, використовуйте Encoding.Default.WindowsCodePage(у моєму випадку 1250 - і, на жаль, немає заздалегідь визначеного класу кодування CP1250, але об'єкт можна отримати як Encoding.GetEncoding(1250)).

Encoding.ASCII 7-бітний, тому в моєму випадку він також не працює:

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

... і чому для цього слід використовувати кодування UTF-8 ...

Кодування по замовчуванням вводить в оману: .NET використовує UTF-8 всюди , як реальний дефолт (8bit кодувань застарівають до кінця 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 і може також використовуватися безпосередньо або як

var utf8 = Encoding.UTF8 as UTF8Encoding;

... але він використовується не завжди

Кодування для байтових масивів повинно "просто працювати" в Unicode в західних країнах, але як тільки ви перемістите свою програму в деякі менш підтримувані регіони (наприклад, тут у Східній Європі), це справжній безлад: у Чехії за замовчуванням Windows використовують (у 2020 році!) MS нестандартний 852 (він же латинський-2) для консолі, 1250 як Windows OEM, UTF-8 (65001) як .NET (та інші) нові за замовчуванням, і ми повинні мати на увазі, що деякі західні 8-бітові ЄС Дані є ще у 1252 році, тоді як старий 8-бітовий західний стандарт для Східної Європи був ISO-8859-2 (він же латинський-2, але НЕ такий самий латинський-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і stringє UTF-16 за визначенням.
Том Блоджет

Так, за замовчуванням є UTF-16. Я не роблю припущень щодо кодування вхідного рядка.
Mandar Sudame

Немає тексту, а закодований текст. Ваш вхід типу stringі, отже, UTF-16. UTF-16 не є типовим; вибору в цьому немає. Потім ви розділитесь на char[]кодові одиниці UTF-16. Потім ви викликаєте Convert.ToByte (Char) , який просто відбувається, щоб перетворити U + 0000 в U + 00FF в ISO-8859-1 , і керує будь-якими іншими кодовими точками.
Том Блоджет

Має сенс. Дякуємо за роз’яснення. Оновлення моєї відповіді.
Mandar Sudame

1
Я думаю, ви все ще не вистачаєте кількох важливих моментів. Зосередьтеся на charтому, що це 16 біт та Convert.ToByte()викиньте половину з них.
Том Блоджет


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

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 (в C # land називається "Unicode").

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


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

}

Ключовим моментом є те, що перетворення рядка в байт [] можна здійснити за допомогою LINQ:

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

І зворотне:

.Select ( character => ( char ) character ).ToArray () )

3

Хтось бачить якусь причину, чому б цього не зробити?

mystring.Select(Convert.ToByte).ToArray()

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


2

Ви можете використовувати API MemoryMarshal для дуже швидкого та ефективного перетворення. 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();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.