Як отримати послідовне байтне представлення рядків у C #, не вказуючи кодування вручну?


2189

Як перетворити stringдо byte[]в .NET (C #), вручну вказавши конкретну кодування?

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

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


23
Кожен рядок зберігається як масив байтів? Чому я просто не можу мати ці байти?
Агнел Куріан

135
Кодування - це те, що відображає символів у байтах. Наприклад, в ASCII літера "А" відображається на номер 65. У іншому кодуванні воно може бути не однаковим. Підхід на високому рівні до рядків у рамках .NET робить це в значній мірі нерелевантним (за винятком цього випадку).
Лукас Джонс

20
Щоб грати в захисника диявола: Якщо ви хотіли отримати байти рядка в пам'яті (як .NET використовує їх) і якось маніпулювати ними (тобто CRC32), і НІКОЛИ ніколи не хотів розшифрувати його назад у початковий рядок ... це Не зрозуміло, чому ви дбаєте про кодування або про те, як вибрати, яке саме використовувати.
Грег

78
Здивований, ніхто ще не посилався на
Беван

28
Char не є байтом, а байт - це не char. Значок - це і ключ до таблиці шрифтів, і лексична традиція. Рядок - це послідовність знаків. (Слова, абзаци, речення та назви також мають свої лексичні традиції, які виправдовують власні визначення типів, але я відступаю). Як і цілі числа, числа з плаваючою комою та все інше, символи кодуються в байти. Був час, коли кодування було простим одне до одного: ASCII. Однак, щоб вмістити всю символіку людини, 256 перестановок байту було недостатньо, і кодування було розроблено для вибіркового використання більшої кількості байтів.
Джордж

Відповіді:


1855

Всупереч відповідям тут, НЕ потрібно турбуватися про кодування, якщо байти не потрібно інтерпретувати!

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

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

Просто зробіть це замість цього:

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

Поки ваша програма (або інші програми) не намагаються якось інтерпретувати байти, чого ви, очевидно, не згадували про те, що маєте намір робити, то в цьому підході немає нічого поганого! Турбота про кодування просто ускладнює ваше життя без реальних причин.

Додаткова вигода від цього підходу:

Не має значення, якщо рядок містить недійсні символи, адже ви все одно можете отримати дані та реконструювати оригінальну рядок у будь-якому разі!

Він буде закодований і розшифрований точно так само, тому що ви просто дивитесь на байти .

Якщо ви використовували певне кодування, це створювало б проблеми з кодуванням / декодуванням недійсних символів.


247
Неприємне в цьому - те, що GetStringі GetBytesпотрібно виконувати в системі з однаковою цінністю для роботи. Тому ви не можете використовувати це для отримання байтів, які ви хочете перетворити на рядок в іншому місці. Тому мені важко придумати ситуації, коли я хотів би цим скористатися.
CodesInChaos

72
@CodeInChaos: Як я вже сказав, вся справа в тому, якщо ви хочете використовувати його в тій же системі, з тим самим набором функцій. Якщо ні, то не слід його використовувати.
користувач541686

193
-1 Я гарантую, що хтось (хто не розуміє байтів проти символів) захоче перетворити їх рядок у байтовий масив, вони перейдуть в Google і прочитають цю відповідь, і вони зроблять неправильну справу, оскільки майже всі випадків, кодування є релевантним.
artbristol

401
@artbristol: Якщо їм не можна заважати читати відповідь (чи інші відповіді ...), то мені шкода, тоді немає кращого способу спілкуватися з ними. Я, як правило, вибираю відповідь на ОП, а не намагаюся вгадати, що можуть зробити інші з моєю відповіддю - ОП має право знати, і те, що хтось може зловживати ножем, не означає, що нам потрібно ховати всі ножі в світі. для себе. Хоча якщо ви не згодні, це теж добре.
користувач541686

185
Ця відповідь помилкова на багатьох рівнях, але головне через те, що вона заявила "вам НЕ потрібно турбуватися про кодування!". Два способи, GetBytes та GetString є зайвими настільки, наскільки вони просто повторні реалізації того, що вже роблять Encoding.Unicode.GetBytes () та Encoding.Unicode.GetString (). Заява "Поки ваша програма (або інші програми) не намагаються інтерпретувати байти" також є принципово хибною, оскільки неявно вони означають, що байти слід інтерпретувати як Unicode.
Девід

1108

Це залежить від кодування вашої рядка ( 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).


14
Але, чому слід враховувати кодування? Чому я не можу просто отримати байти, не бачивши, яке кодування використовується? Навіть якщо це було потрібно, чи не повинен об'єкт String сам знати, що кодування використовується, і просто скидати те, що є в пам'яті?
Агнель Куріан

57
Рядки .NET завжди кодуються як Unicode. Тому використовуйте System.Text.Encoding.Unicode.GetBytes (); щоб отримати набір байтів, які .NET використовував би для представлення символів. Однак чому б ви цього хотіли? Я рекомендую UTF-8, особливо коли більшість символів у західному наборі латині.
AnthonyWJones

8
Також: точні байти, що використовуються всередині рядка , не мають значення, якщо система, яка їх отримує, не обробляє це кодування або обробляє його як неправильне кодування. Якщо це все в .Net, навіщо взагалі перетворювати в масив байтів. В іншому випадку краще бути чітким з кодуванням
Joel Coehoorn,

11
@Joel, будьте обережні з System.Text.Encoding.Default, оскільки це може бути різним на кожній машині, яку він працює. Ось чому рекомендується завжди вказувати кодування, наприклад UTF-8.
Ясен

25
Кодування вам не потрібно, якщо ви (або хтось інший) насправді не маєте наміру (-ла) інтерпретувати дані, замість того, щоб трактувати їх як загальний "блок байтів". Для таких речей, як стиснення, шифрування тощо, турбуватися про кодування безглуздо. Дивіться мою відповідь, як це зробити, не турбуючись про кодування. (Я, можливо, дав -1 за те, що вам потрібно турбуватися про кодування, коли ви цього не робите, але я не відчуваю особливої ​​уваги сьогодні.: P)
user541686

285

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

Не вигадуйте колесо, якщо вам не доведеться ...


14
Якщо прийнята відповідь буде змінена для запису, це відповідь Мехрдада в цей поточний час і дату. Сподіваємось, ОП перегляне це і прийме краще рішення.
Томас Едінг

7
в принципі добре, але кодування повинно System.Text.Encoding.Unicodeбути еквівалентним відповіді Мехрдада.
Джодрелл

5
Запитання було відредаговано безперечно з початкової відповіді, тому, можливо, моя відповідь трохи застаріла. Я ніколи не мав на меті дати еквівалент еквівалента відповіді Мехрдада, але дав розумний спосіб зробити це. Але, ви можете мати рацію. Однак фраза "отримати те, в якому байті рядок було збережено" в первісному питанні є дуже неточною. Зберігається, де? В пам'яті? На диску? Якби в пам’яті, System.Text.Encoding.Unicode.GetBytesпевно , було б точніше.
Ерік А. Брандстадмун

7
@AMissico, ваша пропозиція є помилковою, якщо ви не впевнені, що ваша строка сумісна з кодуванням системи за замовчуванням (рядок, що містить лише символи ASCII у вашій застарілій системі charset). Але ніде в ОП це не зазначено.
Фредерік

5
@AMissico Це може призвести до того, що програма дасть різні результати в різних системах . Це ніколи не добре. Навіть якщо це робиться для створення хешу чи чогось іншого (я припускаю, що це означає, що OP означає "шифрувати"), однаковий рядок повинен завжди надавати один і той же хеш.
Nyerguds

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

2
Ви можете використовувати той самий екземпляр BinaryFormatter для всіх цих операцій
Joel Coehoorn,

3
Дуже цікаво. Мабуть, це скине будь-який високий сурогатний символ Unicode. Дивіться документацію про [BinaryFormatter ]

95

Потрібно врахувати кодування, оскільки 1 символ може бути представлений 1 або більше байтами (приблизно до 6), і різні кодування трактуватимуть ці байти по-різному.

Джоел розмістив повідомлення про це:

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


6
"1 символ може бути представлений 1 або більше байтами" Я згоден. Я просто хочу, щоб ті байти незалежно від того, в якому кодуванні є рядок. Єдиний спосіб, який може зберігати рядок у пам'яті, - це в байтах. Навіть символи зберігаються як 1 або більше байт. Я просто хочу, щоб я наклав на них байти.
Агнел Куріан

16
Кодування вам не потрібно, якщо ви (або хтось інший) насправді не маєте наміру (-ла) інтерпретувати дані, замість того, щоб трактувати їх як загальний "блок байтів". Для таких речей, як стиснення, шифрування тощо, турбуватися про кодування безглуздо. Дивіться мою відповідь, як це зробити, не турбуючись про кодування.
користувач541686

9
@Mehrdad - Повністю, але оригінальне запитання, як було сказано, коли я спочатку відповідав, не зазначало, що ОП відбудеться з тими байтами після їх перетворення, а для майбутніх шукачів потрібна інформація навколо цього - це відповідь Джоела доволі приємно - і, як ви заявляєте у своїй відповіді: якщо ви тримаєтесь у світі .NET і використовуєте свої методи для перетворення в / з, ви раді. Як тільки ви вийдете за межі цього, кодування буде мати значення.
Джаф - Бен Дюгід

Одна кодова точка може бути представлена ​​до 4-х байт. (Один код коду UTF-32, сурогатна пара UTF-16 або 4 байти UTF-8.) Значення, для яких UTF-8 знадобиться більше 4 байт, знаходяться поза межами діапазону 0x0..0x10FFFF Unicode. ;-)
DevSolar

89

Це популярне питання. Важливо зрозуміти, що задає автор питання, і чим він відрізняється від того, що, мабуть, є найпоширенішою потребою. Щоб відмовити від неправильного використання коду там, де він не потрібен, я відповів першим пізніше.

Загальна потреба

Кожна рядок має набір символів та кодування. Коли ви перетворюєте 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 # :

Для обробки символів і рядків у 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 #:

[Для] виразу рядка типу, ... ініціалізатор обчислює адресу першого символу в рядку.

Для цього компілятор записує код пропускання через інші частини об'єкта рядка 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, результат залежить від витривалості машини. Але автор питання цим не займається.


3
@Jan Це правильно, але довжина рядка вже дає кількість кодових одиниць (не кодових точок).
Том Блоджет

1
Дякуємо, що вказали на це! З MSDN: " LengthВластивість [of String] повертає кількість Charоб'єктів у цьому випадку, а не кількість символів Unicode." Отже, ваш прикладний код правильний як написаний.
Ян Геттіч

1
@supercat "Тип char являє собою кодову одиницю UTF-16, а тип рядка являє собою послідовність кодових одиниць UTF-16." --_ C # 5 Специфікація ._ Хоча, так, немає нічого, що не заважає недійсному рядку Unicode:new String(new []{'\uD800', '\u0030'})
Том Блоджет

1
@TomBlodget: Цікаво, якщо один приймає екземпляри Globalization.SortKey, витягує KeyDataі пакети отримані байти з кожного в String[два байта на символ, MSB перший ], викликаючи String.CompareOrdinalпри результуючих рядків буде значно швидше , ніж виклик SortKey.Compareпо екземплярах SortKey, або навіть закликаючи memcmpці інстанції. Враховуючи це, мені цікаво, чому KeyDataповертає Byte[]скоріше, ніж а String?
supercat

1
На жаль, правильна відповідь, але роки пізно, ніколи не матиме стільки голосів, скільки прийнято. Завдяки TL; DR люди подумають, що прийнята відповідь несе. скопіюйте та голосуйте за нього.
Мартін Каподічі

46

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

Я торкнуся Вашого подальшого запитання: навіщо вам вибрати кодування? Чому ви не можете отримати це від самого класу string?

Відповідь у двох частинах.

Перш за все, байти, використовувані внутрішньо класом рядків , не мають значення , і коли ви припускаєте, що вони є, ви, швидше за все, вводите помилку.

Якщо ваша програма повністю перебуває у світі .Net, то вам взагалі не потрібно турбуватися про отримання байтових масивів для рядків, навіть якщо ви надсилаєте дані через мережу. Натомість використовуйте .Net серіалізацію, щоб турбуватися про передачу даних. Ви вже не переживаєте за фактичні байти: формат серіалізації робить це за вас.

З іншого боку, що, якщо ви відправляєте ці байти кудись, що ви не можете гарантувати, отримає дані з .Net серіалізованого потоку? У цьому випадку вам обов'язково потрібно потурбуватися про кодування, оскільки, очевидно, ця зовнішня система дбає. Отже, внутрішні байти, використовувані рядком, не мають значення: вам потрібно вибрати кодування, щоб ви могли бути чітко про це кодування на приймальному кінці, навіть якщо це те саме кодування, яке використовує внутрішньо .Net.

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

Це підводить мене до другої частини ... вибираючи Unicodeкодування буде говорити .Net , щоб використовувати основні байти. Вам потрібно вибрати це кодування, тому що коли виходить якийсь новомодний Unicode-Plus, час виконання .Net повинен мати можливість вільно користуватися цією новою, кращою моделлю кодування, не порушуючи програму. Але на даний момент (і передбачуване майбутнє) саме вибір кодування Unicode дає вам те, що ви хочете.

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


9
У .NET є області, де ви повинні отримати масиви байтів для рядків. Багато класів криптирографії .NET містять такі методи, як ComputeHash (), які приймають байтовий масив або потік. У вас немає альтернативи, крім як спершу перетворити рядок у байтовий масив (вибравши Encoding), а потім необов'язково загортати його в потік. Однак, поки ви вибираєте кодування (тобто UTF8) палицю, з цим проблем немає.
Ясен

44

Просто щоб показати , що звук Mehrdrad в відповідь працює, його підхід може навіть зберігатися непарні символи сурогатних (багато з яких були направлені проти моєї відповіді, але про які все однаково винні, наприклад 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

谢谢!


3
Чи не повинні сурогати з'являтися парами, щоб утворювати дійсні кодові точки? Якщо це так, я можу зрозуміти, чому дані підлягають підробці.
dtanders

1
@dtanders Так, це теж мої думки, вони повинні з'являтися парами, не парні сурогатні персонажі просто трапляються, якщо ви навмисно ставите їх на рядок і робите їх непарними. Чого я не знаю, це те, чому інші дияволи продовжують працювати на тому, що ми повинні використовувати підхід, що усвідомлює кодування, замість цього, оскільки вони вважають, що підхід серіалізації ( моя відповідь була прийнятою відповіддю протягом більше 3 років) не тримає неподілених сурогатний персонаж неушкоджений. Але вони забули перевірити, чи їхні рішення, що знають про кодування, також не утримують нерозділений сурогатний персонаж, іронія ツ
Майкл Буен

Якщо є бібліотека серіалізації, яка використовує System.Buffer.BlockCopyвнутрішньо, всі аргументи людей, що кодують та обстоюють, будуть суперечливими
Майкл Буен

2
@MichaelBuen Мені здається, головне питання в тому, що ти великими жирними літерами говориш, що щось не має значення, а не говорити, що в їхньому випадку це не має значення. Як результат, ви заохочуєте людей, які дивляться на вашу відповідь, робити основні помилки програмування, які в майбутньому можуть викликати фрустрацію. Непарні сурогати недійсні в рядку. Це не масив char, тому має сенс, що перетворення рядка в інший формат призведе до помилки FFFDцього символу. Якщо ви хочете виконати ручну обробку рядків, використовуйте char [], як рекомендується.
Trisped

2
@dtanders: A System.String- незмінна послідовність Char; .NET завжди дозволяв Stringбудувати об'єкт з будь-якого Char[]та експортувати його вміст до Char[]таких же значень, навіть якщо оригінал Char[]містить не парні сурогати.
supercat

41

Спробуйте це, набагато менше коду:

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

Тоді спробуйте це System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);, і плачте! Він буде працювати, але System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Lengthпоки"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
mg30rg

9
@ mg30rg: Чому ви вважаєте, що ваш приклад дивний? Безумовно, що в кодуванні змінної ширини не всі символи мають однакові байтові довжини. Що не так з ним?
Влад

@Vlad Більш достовірним коментарем тут є те, що як закодовані символи унікоду (так, як байти), символи, які включають власні діакритики, дадуть інший результат, ніж діакритики, розбиті на символи модифікатора, додані до символу. Але у iirc існують методи в .net, щоб спеціально розділити їх, щоб дозволити отримати послідовне байтове подання.
Nyerguds

25

Ну, я прочитав усі відповіді, і вони стосувалися використання кодування або однієї про серіалізацію, яка скасовує парні сурогати.

Це погано, коли рядок, наприклад, походить із 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);
}

Замість використання власного методу для перетворення байтового масиву в base64, все, що вам потрібно було зробити, - це використовувати вбудований перетворювач: Convert.ToBase64String (arr);
Макотосан

@Makotosan дякую, але я використовував Convert.ToBase64String(arr); для конверсій base64 byte[] (data) <-> string (serialized data to store in XML file). Але для отримання початкового byte[] (data)мені потрібно було щось зробити з Stringвмістом двійкових даних (це спосіб, яким MSSQL повернув мені це). Так, наведені вище функції призначені для String (binary data) <-> byte[] (easy accessible binary data).
Гман

23

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

Тому що немає такого поняття, як "байти рядка".

Рядок (або в загальному сенсі текст) складається з символів: літер, цифр та інших символів. Це все. Комп’ютери, однак, нічого не знають про персонажів; вони можуть обробляти лише байти. Тому, якщо ви хочете зберігати чи передавати текст за допомогою комп’ютера, вам потрібно перетворити символи в байти. Як ти це робиш? Ось де кодування виходять на сцену.

Кодування - це не що інше, як умова перекладати логічні символи у фізичні байти. Найпростіше і найвідоміше кодування - це ASCII, і це все, що вам потрібно, якщо ви пишете англійською мовою. Для інших мов вам знадобляться більш повні кодування, оскільки будь-який із ароматів Unicode є найбезпечнішим вибором на сьогодні.

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

До речі, я настійно рекомендую вам (і будь-кому, з цього питання) прочитати цю маленьку частину мудрості: Абсолютний мінімум для кожного розробника програмного забезпечення абсолютно, позитивно повинен знати про набори юнікодів та символів (без виправдань!)


2
Дозвольте мені уточнити: кодування було використано для перекладу "привіт світ" у фізичні байти. Оскільки рядок зберігається на моєму комп’ютері, я впевнений, що він повинен зберігатися в байтах. Я просто хочу отримати доступ до цих байтів, щоб зберегти їх на диску або з будь-якої іншої причини. Я не хочу інтерпретувати ці байти. Оскільки я не хочу інтерпретувати ці байти, потреба в кодуванні в цей момент є такою ж помилковою, як і вимагати телефонної лінії для виклику printf.
Агнел Куріан

3
Але знову ж таки, немає поняття переклад тексту в фізичний байт, якщо ви не використовуєте кодування. Звичайно, компілятор зберігає рядки якось у пам’яті - але це просто використання внутрішнього кодування, про яке ви (або хто, крім розробника компілятора) не знаєте. Отже, що б ви не робили, вам потрібно кодування, щоб отримати фізичні байти з рядка.
Конаміман

@Agnel Kurian: Звичайно, правда, що рядок має куди байтів, де зберігається її вміст (UTF-16 afair). Але є вагома причина не дати вам отримати доступ до нього: рядки є незмінними, і якщо ви зможете отримати внутрішній байт [] масив, ви можете також змінити його. Це порушує незмінність, що є життєво важливим, оскільки кілька рядків можуть обмінюватися одними і тими ж даними. Використання кодування UTF-16 для отримання рядка, ймовірно, просто скопіює дані.
олб

2
@Gnafoo, копія байтів зробить.
Агнель Куріан

22

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

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

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

Але, чому слід враховувати кодування? Чому я не можу просто отримати байти, не бачивши, яке кодування використовується? Навіть якщо це було потрібно, чи не повинен об'єкт String сам знати, що кодування використовується, і просто скидати те, що є в пам'яті?
Агнел Куріан

5
Це не завжди працює. Деякі спеціальні персонажі можуть загубитися, використовуючи такий метод, який я знайшов важким шляхом.
Король JB

17

Ви можете використовувати наступний код для перетворення між рядковим і байтовим масивом.

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

VUPце вирішив мою проблему (байт [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd

16

З появою 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класу слід використовувати замість міркування про дані як рядки взагалі.


13

Я не впевнений, але я думаю, що рядок зберігає свою інформацію як масив 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, але це буде неефективно з місцями для зберігання.


10

Ключовим питанням є те, що гліф у рядку займає 32 біти (16 біт для символьного коду), але в байті є лише 8 біт. Зображення «один на один» не існує, якщо ви не обмежитеся рядками, що містять лише символи ASCII. System.Text.Encoding має безліч способів зіставити рядок у байт [], вам потрібно вибрати той, який дозволяє уникнути втрати інформації, і його легко використовувати клієнт, коли їй потрібно зіставити байт [] назад до рядка .

Utf8 - популярне кодування, воно компактне і не втрачає.


3
UTF-8 є компактним лише у тому випадку, якщо більшість ваших персонажів знаходяться в англійському (ASCII) наборі символів. Якби у вас довгий рядок китайських символів, UTF-16 був би більш компактним кодуванням, ніж UTF-8 для цієї рядка. Це тому, що UTF-8 використовує один байт для кодування ASCII, а 3 (а може і 4) в іншому випадку.
Джоел Мюллер

7
Правда. Але як вам не знати про кодування, якщо ви знайомі з поводженням з китайським текстом?
Ганс Пассан

9

Використання:

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

Результат:

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

OP спеціально просить НЕ вказувати кодування ... "без вручну вказувати конкретне кодування"
Фердз

8

Найшвидший спосіб

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

EDIT, як коментує Макотосан, це зараз найкращий спосіб:

Encoding.UTF8.GetBytes(text)

8
ASCIIEncoding ..... не потрібен. Просто бажано використовувати Encoding.UTF8.GetBytes (текст).
Макотосан

8

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

Рядок в .NET представляє текст у вигляді послідовності UTF-16 одиниць коди, так що байти кодуються в пам'яті в UTF-16 вже.

Відповідь Мехрдада

Ви можете використовувати відповідь Мехрдада , але він фактично використовує кодування, тому що символами є UTF-16. Він викликає ToCharArray, який, дивлячись на джерело, створює char[]і безпосередньо копіює пам'ять до нього. Потім він копіює дані в байтовий масив, який також виділяється. Тож під капотом двічі копіюється базовий байт і виділяється масив char, який не використовується після виклику.

Відповідь Тома Блоджета

Відповідь Тома Блоджета на 20-30% швидше, ніж Мехрдад, оскільки він пропускає проміжний крок розподілу масиву знаків і копіювання байтів до нього, але вимагає компіляції з цим /unsafeпараметром. Якщо ви абсолютно не хочете використовувати кодування, я думаю, що це шлях. Якщо ви помістите свій логін для шифрування всередині fixedблоку, вам навіть не потрібно виділяти окремий байтовий масив і копіювати в нього байти.

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

Тому що це правильний спосіб зробити це. stringє абстракцією.

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

Якщо ви використовуєте System.Text.Encoding.Unicode, ваш код буде більш стійким. Вам не потрібно турбуватися про витривалість системи, на якій буде працювати ваш код. Вам не потрібно хвилюватися, чи буде наступна версія CLR використовувати інше внутрішнє кодування символів.

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


Насправді рядок у C # НЕ обмежується лише UTF-16. Щоправда, це те, що він містить вектор 16-бітних одиниць коду, але ці 16-бітні одиниці коду не обмежуються дійсним UTF-16. Але оскільки вони є 16-бітними, вам потрібно кодування (порядок байтів), щоб перетворити їх на 8 біт. Потім рядок може зберігати дані, що не стосуються Unicode, включаючи двійковий код (наприклад, зображення растрового зображення). Її інтерпретують як UTF-16 лише у форматі вводу / виводу та текстових форматах, які роблять таку інтерпретацію.
verdy_p

Таким чином, у рядку C # ви можете безпечно зберігати блок коду, наприклад 0xFFFF або 0xFFFE, навіть якщо вони є не символами в UTF-16, і ви можете зберігати ізольований 0xD800, не супроводжуваний кодовою одиницею, у 0xDC00..0xDFFF (тобто непарні сурогати, які є недійсними в UTF-16). Це ж зауваження стосується рядків у Javascript / ECMAscript та Java.
verdy_p

Коли ви використовуєте "GetBytes", ви, звичайно, не вказуєте кодування, але ви приймаєте на замовлення байт, щоб отримати два байти в специфічній формі для кожного блоку коду, що зберігається локально в рядку. Коли ви створюєте нову рядок з байтів, вам також потрібен перетворювач, не обов'язково UTF-8 до UTF-16, ви можете вставити додаткові 0 у високий байт або спакувати два байти (в MSB першого або LSB першого порядку) у той самий 16-бітний блок коду. Потім рядки є компактною формою для масивів 16-бітних цілих чисел. Зв'язок з "персонажами" - це ще одна проблема. У C # вони не є фактичними типами, оскільки вони все ще представлені у вигляді рядків
verdy_p

7

Найближчий підхід до питання ОП - це Том Блоджет, який фактично йде в об’єкт і витягує байти. Я кажу найближче, тому що це залежить від реалізації об'єкта String.

"Can't I simply get what bytes the string has been stored in?"

Звичайно, але саме тут виникає принципова помилка у питанні. Рядок - це об'єкт, який може мати цікаву структуру даних. Ми це вже знаємо, оскільки це дозволяє зберігати парні сурогати. Він може зберігати довжину. Він може містити вказівник на кожен із парних сурогатів, що дозволяє швидко рахувати. Всі ці додаткові байти не є частиною символьних даних.

Що ви хочете, це байти кожного символу в масиві. І саме тут надходить "кодування". За замовчуванням ви отримаєте UTF-16LE. Якщо вас не цікавлять самі байти, за винятком зворотної поїздки, тоді ви можете вибрати будь-яке кодування, включаючи "за замовчуванням", і перетворити його пізніше (припускаючи ті самі параметри, як, наприклад, кодування за замовчуванням, кодові точки, виправлення помилок , дозволені речі, такі як парні сурогати тощо.

Але навіщо залишати «кодування» до магії? Чому б не вказати кодування, щоб ви знали, які байти ви збираєтеся отримати?

"Why is there a dependency on character encodings?"

Кодування (в цьому контексті) просто означає байти, які представляють вашу рядок. Не байти об'єкта рядка. Ви хотіли, щоб в байтах зберігалася рядок - саме тут питання було задано наївно. Ви хотіли, щоб байти рядка знаходились у суміжному масиві, який представляє рядок, а не всі інші бінарні дані, які може містити об'єкт рядка.

Що означає, як зберігається рядок, не має значення. Ви хочете, щоб рядок "закодовано" в байти в байтовому масиві.

Мені подобається відповідь Тома Блоге, тому що він відвів вас у бік "байтів об'єкта рядка". Хоча це залежить від реалізації, і тому, що він зазирає до внутрішніх даних, можливо, буде важко відновити копію рядка.

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


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

Але було використано перетворення - кожен символ розміщувався у вікні фіксованого розміру (тип символів c #). Однак те, що таке представництво, не має значення, що технічно є відповіддю до ОП. Отже - якщо ви все одно будете конвертувати ... Чому б не "кодувати"?


Ці символи не підтримуються UTF-8 або UTF-16 або навіть UTF-32 для exapmle: 񩱠& (Char) 55906& (Char) 55655. Тож ви можете помилитися, і відповідь Мехрдада - це безпечне перетворення, не враховуючи, який тип кодування використовується.
Mojtaba Rezaeian

Реймоне, символи вже представлені деяким значенням unicode - і всі значення unicode можуть бути представлені усіма utf. Чи є більш тривале пояснення того, про що ви говорите? У якому кодуванні символів існують ці два значення (або 3 ..)?
Джерард ONeill

Вони є недійсними символами, які не підтримуються жодними діапазонами кодування. Це не означає, що вони на 100% марні. Код, який перетворює будь-який тип рядка в його еквівалентний масив байтів незалежно від кодувань, зовсім не є помилковим рішенням і має власні звичаї в бажаних випадках.
Mojtaba Rezaeian

1
Гаразд, тоді я думаю, що ти не розумієш проблеми. Ми знаємо, що це масив, сумісний з унікодом, адже - це .net, ми знаємо, що це UTF-16. Тож цих персонажів там не буде. Ви також не повністю прочитали мій коментар щодо зміни внутрішніх уявлень. String - це об'єкт, а не закодований байтовий масив. Тому я не згоден з вашим останнім твердженням. Ви хочете, щоб код перетворив усі рядки Unicode в будь-яке кодування UTF. Це правильно робить те, що ти хочеш.
Джерард ONeill

Об'єкти - це послідовність даних, спочатку послідовність бітів, яка описує об'єкт у його поточному стані. Отже, всі дані мов програмування можуть бути конвертованими у масив байтів (кожен байт визначає 8 біт), оскільки вам може знадобитися збереження певного стану будь-якого об'єкта в пам'яті. Після зчитування з диска ви можете зберегти і утримувати послідовність байтів у файлі чи пам'яті і передавати їх у вигляді цілого числа, bigint, зображення, рядка Ascii, рядка UTF-8, зашифрованого рядка або вашого власного визначеного типу даних. Тож не можна сказати, що об’єкти - це щось інше, ніж послідовність байтів.
Мойтаба Резайан

6

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

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

3

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

[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

Ось моя небезпечна реалізація 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
    }
}

Чи значення RuntimeHelpers.OffsetToStringDataкратного 8 у версії Itanium .NET? Тому що в іншому випадку це не вдасться через нерівні читання.
Джон Ханна

хіба не було б простіше викликати memcpy? stackoverflow.com/a/27124232/659190
Jodrell

2

Просто скористайтеся цим:

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

2
... і втратити всіх символів зі стрибком вище 127. На моїй рідній мові цілком вірно написати "Árvíztűrő tükörfúrógép.". System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();поверне "Árvizturo tukörfurogép."втрачаючу інформацію, яку неможливо отримати. (І я ще не згадував азіатські мови, де ви втратите всіх символів.)
mg30rg

2

Рядок може бути перетворений в байтовий масив кількома різними способами, завдяки наступному факту: .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

Це залежить від того, для чого потрібно байти

Це тому, що, як так влучно сказав Тайлер , "Струни - це не чисті дані. Вони також мають інформацію ". У цьому випадку інформація - це кодування, яке передбачалося при створенні рядка.

Якщо припустити, що у вас є двійкові дані (а не текстові), що зберігаються в рядку

Це ґрунтується на коментарі ОП до його власного запитання, і це правильне запитання, якщо я розумію підказки ОП щодо випадку використання.

Зберігання двійкових даних у рядках - це, мабуть, неправильний підхід через вищезазначене кодування! Яка б програма чи бібліотека не зберігала, що бінарні дані в string(замість byte[]масиву, який був би більш підходящим), вже програли битву до її початку. Якщо вони надсилають вам байти у запиті / відповіді REST або будь-що, що повинно передавати рядки, Base64 був би правильним підходом.

Якщо у вас є текстовий рядок з невідомим кодуванням

Усі інші відповіли неправильно на це запитання.

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


2

На запитання, що ви маєте намір робити з байтами, ви відповіли :

Я збираюся це зашифрувати. Я можу це зашифрувати, не перетворюючи, але я все одно хотів би знати, чому кодування тут грає. Просто дайте мені байти - це те, що я кажу.

Незалежно від того, чи плануєте ви надсилати ці зашифровані дані через мережу, завантажувати їх назад у пам'ять пізніше або відпарювати в інший процес, ви явно маєте намір розшифрувати їх у якийсь момент. У цьому випадку відповідь полягає в тому, що ви визначаєте протокол зв'язку. Протокол зв'язку не повинен визначатися з точки зору деталей реалізації вашої мови програмування та пов'язаного з цим часу виконання. Для цього є кілька причин:

  • Можливо, вам доведеться спілкуватися з процесом, реалізованим іншою мовою або під час виконання. (Це може включати сервер, який працює на іншій машині або надсилає рядок, наприклад, клієнту браузера JavaScript.)
  • Програма в майбутньому може бути повторно реалізована іншою мовою або під час виконання.
  • Реалізація .NET може змінити внутрішнє представлення рядків. Ви можете подумати, що це звучить надумано, але це дійсно сталося в Java 9, щоб зменшити використання пам'яті. Немає причин. NET не міг наслідувати його. Скіт стверджує, що UTF-16, напевно, не є оптимальним сьогодні, викликають емоджи та інші блоки Unicode, для того, щоб представити більше 2 байтів, збільшуючи ймовірність того, що внутрішнє представлення може змінитися в майбутньому.

Для спілкування (або з абсолютно розрізненою процесом або з однієї і тієї ж програмою в майбутньому), необхідно визначити свій протокол строго звести до мінімуму труднощі роботи з ним або випадковим створенням помилок. Залежно від внутрішнього представлення .NET не є суворим, чітким або навіть гарантованим послідовним визначенням. Стандартне кодування - це суворе визначення, яке не підведе вас у майбутньому.

Іншими словами, ви не можете задовольнити свою вимогу послідовності, не вказавши кодування.

Ви, звичайно, можете скористатися UTF-16 безпосередньо, якщо виявите, що ваш процес працює значно краще, оскільки .NET використовує його внутрішньо або з будь-якої іншої причини, але вам потрібно вибрати це кодування чітко і виконувати ці перетворення явно у вашому коді, а не залежно від цього про внутрішню реалізацію .NET

Тому виберіть кодування та скористайтеся ним:

using System.Text;

// ...

Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")

Як бачите, також фактично менше коду просто використовувати вбудовані об'єкти кодування, ніж реалізувати власні методи читання / запису.


1

Два способи:

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

Я, як правило, використовую нижній частіше, ніж верхній, не орієнтував їх на швидкість.


4
Що з багатобайтовими символами?
Агнель Куріан

c.ToByte () приватний: S
Ходор

@AgnelKurian Msdn каже: "Цей метод повертає непідписане байтове значення, яке представляє числовий код об'єкта Char, переданий йому. У .NET Framework об'єкт Char - це 16-бітове значення. Це означає, що метод підходить для повернення числові коди символів в діапазоні символів ASCII або в діапазонах управління Unicode C0 та базовій латинській мові, а також елементами управління C1 та Latin-1, від U + 0000 до U + 00FF. "
mg30rg

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