Bagaimana cara mendapatkan representasi byte string yang konsisten dalam C # tanpa secara manual menentukan penyandian?

2190

Bagaimana cara mengonversikan stringke byte[]dalam .NET (C #) tanpa secara manual menentukan pengkodean tertentu?

Saya akan mengenkripsi string. Saya dapat mengenkripsi tanpa mengubah, tetapi saya masih ingin tahu mengapa encoding datang untuk bermain di sini.

Juga, mengapa pengkodean harus dipertimbangkan? Tidak bisakah saya mendapatkan byte apa yang telah disimpan oleh string? Mengapa ada ketergantungan pada pengkodean karakter?

Agnel Kurian
sumber
23
Setiap string disimpan sebagai array byte, bukan? Mengapa saya tidak bisa hanya memiliki byte itu?
Agnel Kurian
135
Pengkodean adalah apa yang memetakan karakter ke byte. Misalnya, dalam ASCII, huruf 'A' memetakan ke nomor 65. Dalam penyandian yang berbeda, mungkin tidak sama. Pendekatan tingkat tinggi untuk string yang diambil dalam kerangka .NET membuat ini sebagian besar tidak relevan, (kecuali dalam kasus ini).
Lucas Jones
20
Untuk memainkan advokat iblis: Jika Anda ingin mendapatkan byte dari string di-memori (seperti. NET menggunakannya) dan memanipulasi mereka entah bagaimana (yaitu CRC32), dan TIDAK PERNAH ingin menguraikan kembali ke string asli ... itu tidak langsung mengapa Anda peduli tentang penyandian atau bagaimana Anda memilih mana yang akan digunakan.
Greg
79
Terkejut belum ada yang memberikan tautan ini: joelonsoftware.com/articles/Unicode.html
Bevan
28
Char bukan byte dan byte bukan char. Char adalah kunci dari tabel font dan tradisi leksikal. String adalah urutan karakter. (Sebuah kata, paragraf, kalimat, dan judul juga memiliki tradisi leksikal mereka sendiri yang membenarkan definisi tipe mereka sendiri - tetapi saya ngelantur). Seperti integer, angka floating point, dan yang lainnya, karakter dikodekan ke dalam byte. Ada saat ketika pengkodean itu sederhana 1-1: ASCII. Namun, untuk mengakomodasi semua simbologi manusia, 256 permutasi byte tidak cukup dan pengkodean dirancang untuk secara selektif menggunakan lebih banyak byte.
George

Jawaban:

1855

Bertentangan dengan jawaban di sini, Anda TIDAK perlu khawatir tentang penyandian jika byte tidak perlu ditafsirkan!

Seperti yang Anda sebutkan, tujuan Anda adalah, sederhana, untuk "mendapatkan byte apa yang disimpan oleh string" .
(Dan, tentu saja, untuk dapat membangun kembali string dari byte.)

Untuk tujuan-tujuan itu, sejujurnya saya tidak mengerti mengapa orang-orang terus mengatakan kepada Anda bahwa Anda perlu pengkodean. Anda tentu TIDAK perlu khawatir tentang pengkodean untuk ini.

Lakukan saja ini sebagai gantinya:

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

Selama program Anda (atau program lain) tidak mencoba menafsirkan byte entah bagaimana, yang Anda jelas tidak menyebutkan Anda ingin lakukan, maka tidak ada yang salah dengan pendekatan ini! Khawatir tentang penyandian hanya membuat hidup Anda lebih rumit tanpa alasan yang nyata.

Manfaat tambahan untuk pendekatan ini:

Tidak masalah jika string berisi karakter yang tidak valid, karena Anda tetap bisa mendapatkan data dan merekonstruksi string asli!

Ini akan dikodekan dan didekodekan sama saja, karena Anda hanya melihat byte .

Jika Anda menggunakan pengodean tertentu, itu akan memberi Anda masalah dengan pengodean / decoding karakter yang tidak valid.

pengguna541686
sumber
248
Apa yang jelek tentang yang ini adalah, itu GetStringdan GetBytesperlu dieksekusi pada sistem dengan endianness yang sama untuk bekerja. Jadi Anda tidak dapat menggunakan ini untuk mendapatkan byte yang ingin Anda ubah menjadi string di tempat lain. Jadi saya memiliki waktu yang sulit untuk menemukan situasi di mana saya ingin menggunakan ini.
CodesInChaos
72
@CodeInChaos: Seperti yang saya katakan, inti dari semua ini adalah jika Anda ingin menggunakannya pada sistem yang sama, dengan rangkaian fungsi yang sama. Jika tidak, maka Anda tidak boleh menggunakannya.
user541686
193
-1 Saya menjamin bahwa seseorang (yang tidak mengerti byte dan karakter) akan ingin mengubah string mereka menjadi array byte, mereka akan google dan membaca jawaban ini, dan mereka akan melakukan hal yang salah, karena di hampir semua kasus, penyandian IS relevan.
artbristol
401
@artbristol: Jika mereka tidak mau repot-repot membaca jawabannya (atau jawaban lain ...), maka saya minta maaf, maka tidak ada cara yang lebih baik bagi saya untuk berkomunikasi dengan mereka. Saya umumnya memilih untuk menjawab OP daripada mencoba menebak apa yang mungkin dilakukan orang lain dengan jawaban saya - OP memiliki hak untuk tahu, dan hanya karena seseorang mungkin menyalahgunakan pisau, bukan berarti kita perlu menyembunyikan semua pisau di dunia. Untuk kita sendiri. Padahal jika Anda tidak setuju itu juga baik-baik saja.
user541686
185
Jawaban ini salah pada banyak tingkatan tetapi terutama karena deklerasinya "Anda TIDAK perlu khawatir tentang penyandian!". 2 metode, GetBytes dan GetString tidak berguna karena hanya merupakan implementasi ulang dari apa yang Encoding.Unicode.GetBytes () dan Encoding.Unicode.GetString () sudah dilakukan. Pernyataan "Selama program Anda (atau program lain) tidak mencoba menafsirkan byte" juga pada dasarnya cacat karena secara implisit mereka berarti byte harus ditafsirkan sebagai Unicode.
David
1108

Itu tergantung pada pengkodean string Anda ( ASCII , UTF-8 , ...).

Sebagai contoh:

byte[] b1 = System.Text.Encoding.UTF8.GetBytes (myString);
byte[] b2 = System.Text.Encoding.ASCII.GetBytes (myString);

Contoh kecil mengapa penyandian penting:

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 sama sekali tidak dilengkapi untuk berurusan dengan karakter khusus.

Secara internal, .NET framework menggunakan UTF-16 untuk mewakili string, jadi jika Anda hanya ingin mendapatkan byte yang tepat yang digunakan .NET, gunakan System.Text.Encoding.Unicode.GetBytes (...).

Lihat Pengodean Karakter di .NET Framework (MSDN) untuk informasi lebih lanjut.

bmotmans
sumber
14
Tetapi, mengapa pengkodean harus dipertimbangkan? Mengapa saya tidak bisa mendapatkan byte tanpa harus melihat pengkodean apa yang sedang digunakan? Bahkan jika itu diperlukan, bukankah objek String itu sendiri tahu encoding apa yang sedang digunakan dan hanya membuang apa yang ada dalam memori?
Agnel Kurian
57
Sebuah string .NET selalu dikodekan sebagai Unicode. Jadi gunakan System.Text.Encoding.Unicode.GetBytes (); untuk mendapatkan set byte yang .NET akan gunakan untuk mewakili karakter. Namun mengapa Anda menginginkannya? Saya merekomendasikan UTF-8 terutama ketika sebagian besar karakter berada di set latin barat.
AnthonyWJones
8
Juga: byte tepat yang digunakan secara internal dalam string tidak masalah jika sistem yang mengambilnya tidak menangani pengkodean itu atau menanganinya sebagai pengkodean yang salah. Jika itu semua di dalam. Net, mengapa dikonversi ke array byte sama sekali. Kalau tidak, lebih baik untuk eksplisit dengan pengkodean Anda
Joel Coehoorn
11
@ Joel, Hati-hati dengan System.Text.Encoding.Default karena bisa berbeda pada setiap mesin yang dijalankan. Karena itu disarankan untuk selalu menentukan pengkodean, seperti UTF-8.
Ash
25
Anda tidak perlu penyandian kecuali Anda (atau orang lain) benar-benar bermaksud menginterpretasikan data, alih-alih memperlakukannya sebagai "blok byte" umum. Untuk hal-hal seperti kompresi, enkripsi, dll., Mengkhawatirkan pengkodean tidak ada artinya. Lihat jawaban saya untuk cara melakukan ini tanpa khawatir tentang pengkodean. (Saya mungkin telah memberikan -1 untuk mengatakan Anda perlu khawatir tentang pengkodean ketika Anda tidak, tapi saya tidak merasa sangat jahat hari ini.: P)
user541686
285

Jawaban yang diterima sangat, sangat rumit. Gunakan kelas .NET yang disertakan untuk ini:

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

Jangan menemukan kembali roda jika Anda tidak perlu ...

Erik A. Brandstadmoen
sumber
14
Jika jawaban yang diterima diubah, untuk tujuan rekaman, itu adalah jawaban Mehrdad pada waktu dan tanggal saat ini. Semoga OP akan meninjau kembali ini dan menerima solusi yang lebih baik.
Thomas Eding
7
bagus pada prinsipnya tetapi, pengkodeannya harus System.Text.Encoding.Unicodesetara dengan jawaban Mehrdad.
Jodrell
5
Pertanyaannya telah diedit jutaan kali sejak jawaban aslinya, jadi, mungkin jawaban saya agak ketinggalan jaman. Saya tidak pernah berniat memberikan pengasingan yang setara dengan jawaban Mehrdad, tetapi memberikan cara yang masuk akal untuk melakukannya. Tapi, Anda mungkin benar. Namun, frasa "dapatkan byte apa yang disimpan oleh string" dalam pertanyaan aslinya sangat tidak aman. Disimpan, dimana? Dalam kenangan? Pada disk? Jika di memori, System.Text.Encoding.Unicode.GetBytesmungkin akan lebih tepat.
Erik A. Brandstadmoen
7
@ AMissico, saran Anda buggy, kecuali jika Anda yakin string Anda kompatibel dengan pengkodean default sistem Anda (string yang hanya berisi karakter ASCII di charset legacy default sistem Anda). Tapi tidak ada tempat OP menyatakan itu.
Frédéric
5
@ AMissico Ini dapat menyebabkan program memberikan hasil yang berbeda pada sistem yang berbeda . Itu tidak pernah merupakan hal yang baik. Bahkan jika itu untuk membuat hash atau sesuatu (saya berasumsi itulah yang dimaksud OP dengan 'mengenkripsi'), string yang sama tetap harus selalu memberikan hash yang sama.
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());
Michael Buen
sumber
2
Anda bisa menggunakan contoh BinaryFormatter yang sama untuk semua operasi itu
Joel Coehoorn
3
Sangat menarik. Tampaknya itu akan menjatuhkan karakter Unicode pengganti tinggi. Lihat dokumentasi di [BinaryFormatter ]
95

Anda perlu mempertimbangkan penyandian, karena 1 karakter dapat diwakili oleh 1 atau lebih byte (hingga sekitar 6), dan penyandian yang berbeda akan memperlakukan byte ini secara berbeda.

Joel memiliki posting tentang ini:

Minimum Mutlak Setiap Pengembang Perangkat Lunak, Sepenuhnya Pasti Harus Tahu Tentang Unicode dan Karakter (Tidak Ada Alasan!)

Zhaph - Ben Duguid
sumber
6
"1 karakter dapat diwakili oleh 1 atau lebih byte" Saya setuju. Saya hanya ingin byte itu terlepas dari apa pengkodean string. Satu-satunya cara string dapat disimpan dalam memori adalah dalam byte. Bahkan karakter disimpan sebagai 1 byte atau lebih. Saya hanya ingin mendapatkan byte.
Agnel Kurian
16
Anda tidak perlu penyandian kecuali Anda (atau orang lain) benar-benar bermaksud menginterpretasikan data, alih-alih memperlakukannya sebagai "blok byte" umum. Untuk hal-hal seperti kompresi, enkripsi, dll., Mengkhawatirkan pengkodean tidak ada artinya. Lihat jawaban saya untuk cara melakukan ini tanpa khawatir tentang pengkodean.
user541686
9
@Mehrdad - Benar-benar, tetapi pertanyaan awal, seperti yang dinyatakan ketika saya awalnya menjawab, tidak menyatakan OP apa yang akan terjadi dengan byte-byte tersebut setelah mereka mengonversinya, dan bagi para pencari di masa depan informasi seputar yang bersangkutan - ini adalah dicakup oleh jawaban Joel dengan cukup baik - dan ketika Anda menyatakan dalam jawaban Anda: asalkan Anda tetap berada di dunia .NET, dan menggunakan metode Anda untuk mengonversi ke / dari, Anda bahagia. Segera setelah Anda melangkah keluar, pengodean akan menjadi masalah.
Zhaph - Ben Duguid
Satu titik kode dapat direpresentasikan hingga 4 byte. (Satu unit kode UTF-32, pasangan pengganti UTF-16, atau 4 byte UTF-8.) Nilai-nilai yang diperlukan UTF-8 lebih dari 4 byte berada di luar kisaran Unicode 0x0..0x10FFFF. ;-)
DevSolar
89

Ini pertanyaan populer. Penting untuk memahami apa yang ditanyakan oleh penulis pertanyaan, dan berbeda dari apa yang mungkin merupakan kebutuhan paling umum. Untuk mencegah penyalahgunaan kode yang tidak diperlukan, saya sudah jawab yang pertama.

Kebutuhan Umum

Setiap string memiliki rangkaian karakter dan pengodean. Saat Anda mengonversi System.Stringobjek ke array, System.ByteAnda masih memiliki kumpulan karakter dan pengodean. Untuk sebagian besar penggunaan, Anda akan tahu set karakter dan penyandian yang Anda butuhkan dan .NET membuatnya mudah untuk "menyalin dengan konversi." Pilih saja Encodingkelas yang sesuai .

// using System.Text;
Encoding.UTF8.GetBytes(".NET String to byte array")

Konversi mungkin perlu menangani kasus di mana karakter target yang ditetapkan atau pengodean tidak mendukung karakter yang ada di sumber. Anda memiliki beberapa pilihan: pengecualian, penggantian atau lewati. Kebijakan default adalah mengganti '?'.

// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100")); 
                                                      // -> "You win ?100"

Jelas, konversi tidak harus rugi!

Catatan: Untuk System.Stringrangkaian karakter sumber adalah Unicode.

Satu-satunya hal yang membingungkan adalah .NET menggunakan nama set karakter untuk nama satu pengkodean set karakter tertentu. Encoding.Unicodeharus dipanggil Encoding.UTF16.

Itu saja untuk sebagian besar penggunaan. Jika itu yang Anda butuhkan, berhenti membaca di sini. Lihat artikel Joel Spolsky yang menyenangkan jika Anda tidak mengerti apa itu encoding.

Kebutuhan Khusus

Sekarang, penulis pertanyaan bertanya, "Setiap string disimpan sebagai array byte, kan? Mengapa saya tidak bisa hanya memiliki byte itu?"

Dia tidak menginginkan pertobatan.

Dari spesifikasi C # :

Pemrosesan karakter dan string dalam C # menggunakan pengkodean Unicode. Tipe char mewakili unit kode UTF-16, dan tipe string mewakili urutan unit kode UTF-16.

Jadi, kita tahu bahwa jika kita meminta konversi nol (yaitu, dari UTF-16 ke UTF-16), kita akan mendapatkan hasil yang diinginkan:

Encoding.Unicode.GetBytes(".NET String to byte array")

Tetapi untuk menghindari penyebutan encoding, kita harus melakukannya dengan cara lain. Jika tipe data antara dapat diterima, ada jalan pintas konseptual untuk ini:

".NET String to byte array".ToCharArray()

Itu tidak memberikan kita tipe data yang diinginkan tetapi jawaban Mehrdad menunjukkan bagaimana mengubah array Char ini menjadi array Byte menggunakan BlockCopy . Namun, ini menyalin string dua kali! Dan, itu juga secara eksplisit menggunakan kode khusus pengkodean: tipe data System.Char.

Satu-satunya cara untuk mendapatkan byte aktual dari String yang disimpan adalah dengan menggunakan pointer. The fixedpernyataan memungkinkan mengambil alamat nilai-nilai. Dari spesifikasi C #:

[Untuk] ekspresi tipe string, ... initializer menghitung alamat karakter pertama dalam string.

Untuk melakukannya, kompiler menulis kode lompati bagian lain dari objek string RuntimeHelpers.OffsetToStringData. Jadi, untuk mendapatkan byte mentah, buat saja pointer ke string dan salin jumlah byte yang dibutuhkan.

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

Seperti @CodesInChaos tunjukkan, hasilnya tergantung pada endianness dari mesin. Tetapi penulis pertanyaan tidak peduli dengan itu.

Tom Blodget
sumber
3
@ Jan Itu benar tetapi panjang string sudah memberikan jumlah unit kode (bukan codepoint).
Tom Blodget
1
Terima kasih telah menunjukkan itu! Dari MSDN: " LengthProperti Stringmengembalikan jumlah Charobjek dalam contoh ini, bukan jumlah karakter Unicode." Karena itu kode contoh Anda sudah benar seperti yang tertulis.
Jan Hettich
1
@supercat "Tipe char mewakili unit kode UTF-16, dan tipe string mewakili urutan unit kode UTF-16." —_ C # 5 Spesifikasi._ Meskipun, ya, tidak ada yang mencegah string Unicode yang tidak valid:new String(new []{'\uD800', '\u0030'})
Tom Blodget
1
@ TomBlodget: Menariknya, jika seseorang mengambil contoh Globalization.SortKey, mengekstraksi KeyData, dan mengemas byte yang dihasilkan dari masing-masing ke dalam String[dua byte per karakter, MSB pertama ], memanggil String.CompareOrdinalstring yang dihasilkan akan jauh lebih cepat daripada memanggil SortKey.Compareinstance SortKey, atau bahkan memanggil memcmpcontoh-contoh itu. Mengingat itu, saya bertanya-tanya mengapa KeyDatamengembalikan Byte[]bukan String?
supercat
1
Sayangnya, jawaban yang tepat, tetapi bertahun-tahun terlambat, tidak akan pernah memiliki suara sebanyak yang diterima. Karena TL; DR orang akan berpikir batu jawaban yang diterima. copyenpastit dan pilih itu.
Martin Capodici
46

Bagian pertama dari pertanyaan Anda (cara mendapatkan byte) sudah dijawab oleh orang lain: lihat di System.Text.Encodingnamespace.

Saya akan menjawab pertanyaan tindak lanjut Anda: mengapa Anda perlu memilih penyandian? Mengapa Anda tidak bisa mendapatkannya dari kelas string itu sendiri?

Jawabannya ada dalam dua bagian.

Pertama-tama, byte yang digunakan secara internal oleh kelas string tidak penting , dan kapan pun Anda menganggapnya, Anda kemungkinan besar akan memperkenalkan bug.

Jika program Anda sepenuhnya dalam dunia. Net maka Anda tidak perlu khawatir tentang mendapatkan array byte untuk string sama sekali, bahkan jika Anda mengirim data melalui jaringan. Sebagai gantinya, gunakan .Net Serialisasi untuk khawatir tentang pengiriman data. Anda tidak perlu lagi khawatir tentang byte yang sebenarnya: formatter serialisasi melakukannya untuk Anda.

Di sisi lain, bagaimana jika Anda mengirim byte ini di suatu tempat yang Anda tidak dapat menjamin akan menarik data dari aliran serial .Net? Dalam hal ini Anda tentu perlu khawatir tentang pengkodean, karena jelas sistem eksternal ini peduli. Jadi sekali lagi, byte internal yang digunakan oleh string tidak masalah: Anda harus memilih pengkodean sehingga Anda dapat secara eksplisit tentang pengkodean ini pada sisi penerima, bahkan jika itu adalah pengkodean yang sama yang digunakan secara internal oleh .Net.

Saya mengerti bahwa dalam hal ini Anda mungkin lebih suka menggunakan byte aktual yang disimpan oleh variabel string dalam memori jika memungkinkan, dengan gagasan bahwa itu mungkin menghemat pekerjaan menciptakan aliran byte Anda. Namun, saya katakan kepada Anda itu tidak penting dibandingkan dengan memastikan bahwa output Anda dipahami di ujung yang lain, dan untuk menjamin bahwa Anda harus eksplisit dengan pengkodean Anda. Selain itu, jika Anda benar-benar ingin mencocokkan byte internal Anda, Anda sudah bisa memilih Unicodepengkodean, dan mendapatkan penghematan kinerja itu.

Yang membawa saya ke bagian kedua ... memilih Unicodeencoding adalah mengatakan Net menggunakan byte yang mendasari. Anda harus memilih penyandian ini, karena ketika beberapa Unicode-Plus yang baru dilipat keluar, runtime .Net harus bebas untuk menggunakan model penyandian yang lebih baru dan lebih baik ini tanpa merusak program Anda. Tapi, untuk saat ini (dan masa depan yang dapat dilihat), hanya memilih pengkodean Unicode memberi Anda apa yang Anda inginkan.

Penting juga untuk memahami string Anda harus ditulis ulang untuk ditransfer, dan itu melibatkan setidaknya beberapa terjemahan dari pola-bit bahkan ketika Anda menggunakan pengkodean yang cocok . Komputer perlu memperhitungkan hal-hal seperti Big vs Little Endian, urutan byte jaringan, paketisasi, informasi sesi, dll.

Joel Coehoorn
sumber
9
Ada area di .NET di mana Anda harus mendapatkan array byte untuk string. Banyak kelas .NET Cryptrography berisi metode seperti ComputeHash () yang menerima byte array atau stream. Anda tidak memiliki alternatif selain mengubah string ke array byte terlebih dahulu (memilih Encoding) dan kemudian membungkusnya secara stream. Namun selama Anda memilih penyandian (yaitu UTF8) tetap dengan itu tidak ada masalah dengan ini.
Ash
44

Hanya untuk menunjukkan bahwa jawaban suara Mehrdrad berfungsi, pendekatannya bahkan dapat bertahan pada karakter pengganti yang tidak berpasangan (yang banyak diratakan oleh jawaban saya, tetapi semua orang sama-sama bersalah, misalnya System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytesmetode pengkodean itu tidak dapat bertahan sebagai pengganti tinggi. karakter d800misalnya, dan mereka hanya mengganti karakter pengganti tinggi dengan nilaifffd ):

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

Keluaran:

T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74

Cobalah dengan System.Text.Encoding.UTF8.GetBytes atau System.Text.Encoding.Unicode.GetBytes , mereka hanya akan mengganti karakter pengganti yang tinggi dengan nilai fffd

Setiap kali ada gerakan dalam pertanyaan ini, saya masih memikirkan serializer (baik itu dari Microsoft atau dari komponen pihak ke-3) yang dapat bertahan string bahkan mengandung karakter pengganti yang tidak berpasangan; Saya google ini setiap sekarang dan kemudian: serialisasi karakter pengganti berpasangan .NET . Ini tidak membuat saya kehilangan tidur, tetapi agak menjengkelkan ketika kadang-kadang ada seseorang yang mengomentari jawaban saya bahwa itu salah, namun jawaban mereka sama-sama cacat ketika menyangkut karakter pengganti yang tidak berpasangan.

Sial, Microsoft seharusnya baru saja menggunakan System.Buffer.BlockCopy dalam nyaBinaryFormatter

谢谢!

Michael Buen
sumber
3
Bukankah pengganti harus muncul berpasangan untuk membentuk poin kode yang valid? Jika itu masalahnya, saya bisa mengerti mengapa data akan hancur.
dtanders
1
@dtanders Ya, itu juga menurut saya, mereka harus muncul berpasangan, karakter pengganti yang tidak berpasangan terjadi begitu saja jika Anda sengaja meletakkannya di string dan membuatnya tidak berpasangan. Apa yang saya tidak tahu adalah mengapa para pengembang lain terus bersungut-sungut bahwa kita harus menggunakan pendekatan penyandian-sadar sebagai gantinya, karena mereka menganggap pendekatan serialisasi ( jawaban saya , yang merupakan jawaban yang diterima selama lebih dari 3 tahun) tidak membuat pasangan tidak berpasangan karakter pengganti utuh. Tetapi mereka lupa untuk memeriksa bahwa solusi penyandian sadar mereka tidak menjaga karakter pengganti yang tidak berpasangan juga, ironi ツ
Michael Buen
Jika ada perpustakaan serialisasi yang menggunakan System.Buffer.BlockCopyinternal, semua argumen orang-orang pengkodean-advokasi akan diperdebatkan
Michael Buen
2
@MichaelBuen Bagiku masalah utama adalah Anda menggunakan huruf tebal yang mengatakan sesuatu tidak masalah, daripada mengatakan bahwa itu tidak masalah dalam kasus mereka. Sebagai hasilnya, Anda mendorong orang-orang yang melihat jawaban Anda untuk membuat kesalahan pemrograman dasar yang akan menyebabkan orang lain frustrasi di masa depan. Pengganti yang tidak berpasangan tidak valid dalam sebuah string. Ini bukan array char, jadi masuk akal bahwa mengubah string ke format lain akan menghasilkan kesalahan FFFDpada karakter itu. Jika Anda ingin melakukan manipulasi string manual, gunakan char [] seperti yang disarankan.
Ditebangi
2
@dtanders: A System.Stringadalah urutan abadi dari Char; .NET selalu mengizinkan Stringobjek dibuat dari apa pun Char[]dan mengekspor kontennya ke yang Char[]berisi nilai yang sama, bahkan jika aslinya Char[]berisi pengganti yang tidak berpasangan.
supercat
41

Coba ini, jauh lebih sedikit kode:

System.Text.Encoding.UTF8.GetBytes("TEST String");
Nathan
sumber
Lalu coba ini System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);, dan menangis! Ini akan berhasil, tetapi System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Lengthsementara"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
mg30rg
9
@ mg30rg: Mengapa menurut Anda contoh Anda aneh? Tentunya dalam pengodean lebar variabel tidak semua karakter memiliki panjang byte yang sama. Apakah ada yang salah?
Vlad
@ Vlad Komentar yang lebih valid di sini, adalah bahwa sebagai simbol unicode yang dikodekan (jadi, sebagai byte), karakter yang menyertakan diakritik mereka sendiri akan memberikan hasil yang berbeda dari diakritik yang dipisah menjadi simbol pengubah yang ditambahkan ke karakter. Tapi iirc ada metode di .net untuk secara khusus memisahkan mereka, untuk memungkinkan mendapatkan representasi byte yang konsisten.
Nyerguds
25

Yah, saya sudah membaca semua jawaban dan mereka tentang menggunakan encoding atau satu tentang serialisasi yang menjatuhkan pengganti yang tidak berpasangan.

Ini buruk ketika string, misalnya, berasal dari SQL Server mana ia dibangun dari penyimpanan byte array, misalnya, hash kata sandi. Jika kita membuang sesuatu darinya, itu akan menyimpan hash yang tidak valid, dan jika kita ingin menyimpannya dalam XML, kita ingin membiarkannya tetap utuh (karena penulis XML menjatuhkan pengecualian pada pengganti yang tidak berpasangan yang ditemukannya).

Jadi saya menggunakan Base64 encoding array byte dalam kasus seperti itu, tapi hei, di Internet hanya ada satu solusi untuk ini di C #, dan ada bug di dalamnya dan hanya satu cara, jadi saya sudah memperbaiki bug dan menulis kembali prosedur. Inilah Anda, para googler masa depan:

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);
}
Gman
sumber
Alih-alih menggunakan metode kustom Anda untuk mengubah array byte ke base64, yang harus Anda lakukan adalah menggunakan konverter bawaan: Convert.ToBase64String (arr);
Makotosan
@ Makotosan terima kasih, tapi saya menggunakan Convert.ToBase64String(arr); konversi base64 byte[] (data) <-> string (serialized data to store in XML file). Tetapi untuk mendapatkan inisial byte[] (data)saya perlu melakukan sesuatu dengan data binerString yang berisi (itu cara MSSQL mengembalikannya kepada saya). Jadi fungsi di atas adalah untuk . String (binary data) <-> byte[] (easy accessible binary data)
Gman
23

Juga tolong jelaskan mengapa pengkodean harus dipertimbangkan. Tidak bisakah saya mendapatkan byte apa yang telah disimpan oleh string? Mengapa ketergantungan ini pada pengkodean? !!!

Karena tidak ada yang namanya "byte dari string".

String (atau lebih umum, teks) terdiri dari karakter: huruf, angka, dan simbol lainnya. Itu saja. Komputer, bagaimanapun, tidak tahu apa-apa tentang karakter; mereka hanya bisa menangani byte. Oleh karena itu, jika Anda ingin menyimpan atau mengirim teks dengan menggunakan komputer, Anda perlu mengubah karakter menjadi byte. Bagaimana kamu melakukannya? Di sinilah pengkodean datang ke tempat kejadian.

Pengkodean hanyalah konvensi untuk menerjemahkan karakter logis ke byte fisik. Pengkodean yang paling sederhana dan paling dikenal adalah ASCII, dan itu semua yang Anda butuhkan jika Anda menulis dalam bahasa Inggris. Untuk bahasa lain, Anda akan membutuhkan penyandian yang lebih lengkap, karena salah satu dari Unicode ini merupakan pilihan paling aman saat ini.

Jadi, singkatnya, mencoba "mendapatkan byte dari sebuah string tanpa menggunakan penyandian" adalah tidak mungkin seperti "menulis teks tanpa menggunakan bahasa apa pun".

Ngomong-ngomong, saya sangat menyarankan Anda (dan siapa pun, dalam hal ini) untuk membaca kebijaksanaan kecil ini: Minimum Mutlak Setiap Pengembang Perangkat Lunak Sepenuhnya, Sepenuhnya Harus Tahu Tentang Unicode dan Karakter Set (Tanpa Alasan!)

Konamiman
sumber
2
Izinkan saya mengklarifikasi: Pengodean telah digunakan untuk menerjemahkan "hello world" ke byte fisik. Karena string disimpan di komputer saya, saya yakin itu harus disimpan dalam byte. Saya hanya ingin mengakses byte itu untuk menyimpannya di disk atau karena alasan lain. Saya tidak ingin menafsirkan byte ini. Karena saya tidak ingin menafsirkan byte ini, kebutuhan untuk pengkodean pada titik ini adalah salah tempat seperti memerlukan saluran telepon untuk memanggil printf.
Agnel Kurian
3
Tetapi sekali lagi, tidak ada konsep terjemahan teks-ke-fisik-byte kecuali jika Anda menggunakan pengodean. Tentu, kompiler menyimpan string entah bagaimana dalam memori - tetapi hanya menggunakan pengkodean internal, yang Anda (atau siapa pun kecuali pengembang kompiler) tidak tahu. Jadi, apa pun yang Anda lakukan, Anda memerlukan pengodean untuk mendapatkan byte fisik dari sebuah string.
Konamiman
@Agnel Kurian: Tentu saja benar, bahwa string memiliki banyak byte di suatu tempat yang menyimpan kontennya (UTF-16 affair). Tetapi ada alasan bagus untuk mencegah Anda mengaksesnya: string tidak dapat diubah dan jika Anda bisa mendapatkan array byte [] internal, Anda dapat memodifikasinya juga. Ini memecah keabadian, yang sangat penting karena banyak string dapat berbagi data yang sama. Menggunakan pengkodean UTF-16 untuk mendapatkan string mungkin hanya akan menyalin data keluar.
ollb
2
@ Gnafoo, Salinan byte akan dilakukan.
Agnel Kurian
22

C # untuk mengonversi a stringmenjadi bytearray:

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
Shyam sundar shah
sumber
17
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
gkrogers
sumber
Tetapi, mengapa pengkodean harus dipertimbangkan? Mengapa saya tidak bisa mendapatkan byte tanpa harus melihat pengkodean apa yang sedang digunakan? Bahkan jika itu diperlukan, bukankah objek String itu sendiri tahu encoding apa yang sedang digunakan dan hanya membuang apa yang ada dalam memori?
Agnel Kurian
5
Ini tidak selalu berhasil. Beberapa karakter khusus dapat tersesat dalam menggunakan metode seperti yang saya temukan dengan cara yang sulit.
JB King
17

Anda dapat menggunakan kode berikut untuk konversi antara array string dan byte.

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);
Jarvis Stark
sumber
VUPini yang memecahkan masalah saya (byte [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd
16

Dengan munculnya Span<T>dirilis dengan C # 7.2, teknik kanonik untuk menangkap representasi memori yang mendasari string ke array byte yang dikelola adalah:

byte[] bytes = "rubbish_\u9999_string".AsSpan().AsBytes().ToArray();

Mengubahnya kembali harus menjadi non-starter karena itu berarti Anda sebenarnya menafsirkan data entah bagaimana, tetapi demi kelengkapan:

string s;
unsafe
{
    fixed (char* f = &bytes.AsSpan().NonPortableCast<byte, char>().DangerousGetPinnableReference())
    {
        s = new string(f);
    }
}

Nama-nama NonPortableCastdan DangerousGetPinnableReferenceharus melanjutkan argumen bahwa Anda mungkin tidak boleh melakukan ini.

Perhatikan bahwa bekerja dengan Span<T>membutuhkan menginstal paket System.Memory NuGet .

Apapun, pertanyaan asli aktual dan komentar tindak lanjut menyiratkan bahwa memori yang mendasari tidak sedang "ditafsirkan" (yang saya asumsikan berarti tidak dimodifikasi atau dibaca di luar kebutuhan untuk menulis apa adanya), menunjukkan bahwa beberapa implementasi Streamkelas harus digunakan sebagai ganti alasan tentang data sebagai string sama sekali.

John Rasch
sumber
13

Saya tidak yakin, tapi saya pikir string menyimpan informasinya sebagai array Chars, yang tidak efisien dengan byte. Secara khusus, definisi Char adalah "Merupakan karakter Unicode".

ambil contoh contoh ini:

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

Perhatikan bahwa jawaban Unicode adalah 14 byte di kedua contoh, sedangkan jawaban UTF-8 hanya 9 byte untuk yang pertama, dan hanya 7 untuk yang kedua.

Jadi jika Anda hanya ingin byte yang digunakan oleh string, cukup gunakan Encoding.Unicode, tetapi akan tidak efisien dengan ruang penyimpanan.

Ed Marty
sumber
10

Masalah utama adalah bahwa mesin terbang dalam string membutuhkan 32 bit (16 bit untuk kode karakter) tetapi byte hanya memiliki 8 bit untuk cadangan. Pemetaan satu-ke-satu tidak ada kecuali Anda membatasi diri pada string yang hanya berisi karakter ASCII. System.Text.Encoding memiliki banyak cara untuk memetakan string ke byte [], Anda harus memilih satu yang menghindari hilangnya informasi dan yang mudah digunakan oleh klien Anda ketika dia perlu memetakan byte [] kembali ke string. .

Utf8 adalah pengkodean yang populer, ringkas dan tidak lossy.

Hans Passant
sumber
3
UTF-8 kompak hanya jika sebagian besar karakter Anda dalam rangkaian karakter Bahasa Inggris (ASCII). Jika Anda memiliki string panjang karakter Cina, UTF-16 akan menjadi pengkodean yang lebih kompak daripada UTF-8 untuk string itu. Ini karena UTF-8 menggunakan satu byte untuk mengkodekan ASCII, dan 3 (atau mungkin 4) sebaliknya.
Joel Mueller
7
Benar. Tapi, bagaimana Anda bisa tidak tahu tentang penyandian jika Anda terbiasa menangani teks berbahasa Mandarin?
Hans Passant
9

Menggunakan:

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

Hasilnya adalah:

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
mashet
sumber
OP secara khusus meminta untuk TIDAK menentukan suatu pengkodean ... "tanpa secara manual menentukan suatu pengkodean tertentu"
Ferdz
8

Jalan tercepat

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

EDIT sebagai Makotosan berkomentar ini sekarang adalah cara terbaik:

Encoding.UTF8.GetBytes(text)
Alessandro Annini
sumber
8
ASCIIEncoding ..... tidak diperlukan. Cukup menggunakan Encoding.UTF8.GetBytes (teks) lebih disukai.
Makotosan
8

Bagaimana cara mengonversi string ke byte [] dalam .NET (C #) tanpa secara manual menentukan pengkodean tertentu?

Sebuah String dalam. NET mewakili teks sebagai urutan unit kode UTF-16, sehingga byte sudah dikodekan dalam memori di UTF-16.

Jawaban Mehrdad

Kamu bisa menggunakan jawaban Mehrdad , tetapi sebenarnya menggunakan pengodean karena karakternya adalah UTF-16. Itu panggilan ToCharArray yang melihat sumbernya menciptakan char[]dan menyalin memori secara langsung. Kemudian menyalin data ke array byte yang juga dialokasikan. Jadi di bawah tenda itu menyalin byte yang mendasarinya dua kali dan mengalokasikan array char yang tidak digunakan setelah panggilan.

Jawaban Tom Blodget

Jawaban Tom Blodget adalah 20-30% lebih cepat daripada Mehrdad karena melompati langkah menengah mengalokasikan array char dan menyalin byte ke dalamnya, tetapi mengharuskan Anda mengkompilasi dengan /unsafeopsi. Jika Anda benar-benar tidak ingin menggunakan pengodean, saya pikir ini adalah cara untuk pergi. Jika Anda memasukkan login enkripsi Anda di dalam fixedblok, Anda bahkan tidak perlu mengalokasikan array byte terpisah dan menyalin byte ke dalamnya.

Juga, mengapa pengkodean harus dipertimbangkan? Tidak bisakah saya mendapatkan byte apa yang telah disimpan oleh string? Mengapa ada ketergantungan pada pengkodean karakter?

Karena itulah cara yang tepat untuk melakukannya. stringadalah abstraksi.

Menggunakan penyandian dapat memberikan masalah jika Anda memiliki 'string' dengan karakter yang tidak valid, tetapi itu tidak boleh terjadi. Jika Anda memasukkan data ke string dengan karakter yang tidak valid, Anda salah melakukannya. Anda mungkin harus menggunakan array byte atau pengkodean Base64 untuk memulai.

Jika Anda menggunakan System.Text.Encoding.Unicode, kode Anda akan lebih tangguh. Anda tidak perlu khawatir tentang endianness sistem yang akan dijalankan oleh kode Anda. Anda tidak perlu khawatir jika versi CLR berikutnya akan menggunakan pengkodean karakter internal yang berbeda.

Saya pikir pertanyaannya bukan mengapa Anda ingin khawatir tentang pengkodean, tetapi mengapa Anda ingin mengabaikannya dan menggunakan sesuatu yang lain. Pengkodean dimaksudkan untuk mewakili abstraksi string dalam urutan byte. System.Text.Encoding.Unicodeakan memberikan Anda sedikit encoding urutan endian byte dan akan melakukan hal yang sama pada setiap sistem, sekarang dan di masa depan.

Jason Goemaat
sumber
Sebenarnya string dalam C # TIDAK dibatasi hanya UTF-16. Yang benar adalah bahwa ia berisi vektor unit kode 16-bit, tetapi unit kode 16-bit ini tidak terbatas pada UTF-16 yang valid. Tetapi karena mereka 16-bit, Anda perlu encoding (urutan byte) untuk mengubahnya menjadi 8bit. Suatu string kemudian dapat menyimpan data non-Unicode, termasuk kode biner (misalnya gambar bitmap). Ini menjadi ditafsirkan sebagai UTF-16 hanya di I / O dan pemformat teks yang membuat interpretasi tersebut.
verdy_p
Jadi dalam string C #, Anda dapat dengan aman menyimpan unit kode seperti 0xFFFF atau 0xFFFE, bahkan jika mereka bukan karakter dalam UTF-16, dan Anda dapat menyimpan 0xD800 yang terisolasi tidak diikuti oleh unit kode di 0xDC00..0xDFFF (yaitu pengganti tidak berpasangan yang tidak valid dalam UTF-16). Komentar yang sama berlaku untuk string dalam Javascript / ECMAscript dan Java.
verdy_p
Ketika Anda menggunakan "GetBytes", tentu saja Anda tidak menentukan pengkodean, tetapi Anda mengasumsikan urutan byte untuk mendapatkan dua byte dalam specic untuk setiap unit kode yang disimpan secara lokal dalam string. Ketika Anda membangun string baru dari byte, Anda juga memerlukan konverter, tidak harus UTF-8 ke UTF-16, Anda bisa memasukkan 0 ekstra dalam byte tinggi, atau mengemas dua byte (dalam MSB first atau LSB first order) di unit kode 16-bit yang sama. String kemudian bentuk kompak untuk array bilangan bulat 16-bit. Hubungan dengan "karakter" adalah masalah lain, di C # mereka bukan tipe aktual karena mereka masih direpresentasikan sebagai string
verdy_p
7

Pendekatan terdekat dengan pertanyaan OP adalah Tom Blodget, yang sebenarnya masuk ke objek dan mengekstrak byte. Saya katakan paling dekat karena itu tergantung pada implementasi Object String.

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

Tentu, tetapi di situlah kesalahan mendasar dalam pertanyaan muncul. String adalah objek yang dapat memiliki struktur data yang menarik. Kita sudah tahu itu, karena memungkinkan pengganti yang tidak berpasangan untuk disimpan. Mungkin menyimpan panjangnya. Mungkin menyimpan pointer ke masing-masing pengganti 'berpasangan' memungkinkan penghitungan cepat. Dll Semua byte tambahan ini bukan bagian dari data karakter.

Yang Anda inginkan adalah byte setiap karakter dalam sebuah array. Dan di situlah 'encoding' masuk. Secara default Anda akan mendapatkan UTF-16LE. Jika Anda tidak peduli dengan byte itu sendiri kecuali untuk perjalanan pulang pergi maka Anda dapat memilih pengkodean apa pun termasuk 'default', dan mengubahnya kembali nanti (dengan asumsi parameter yang sama seperti apa pengkodean default, titik kode, perbaikan bug , hal-hal yang diperbolehkan seperti pengganti yang tidak berpasangan, dll.

Tapi mengapa membiarkan 'pengkodean' menjadi sihir? Mengapa tidak menentukan pengkodean sehingga Anda tahu byte apa yang akan Anda dapatkan?

"Why is there a dependency on character encodings?"

Pengkodean (dalam konteks ini) berarti byte yang mewakili string Anda. Bukan byte dari objek string. Anda ingin byte yang disimpan oleh string - di sinilah pertanyaan itu ditanyakan secara naif. Anda menginginkan byte string dalam array yang berdekatan yang mewakili string, dan tidak semua data biner lain yang mungkin berisi objek string.

Yang berarti bagaimana string disimpan tidak relevan. Anda ingin string "Dikodekan" menjadi byte dalam array byte.

Saya suka jawaban Tom Bloget karena dia membawa Anda ke arah 'byte dari objek string'. Ini tergantung implementasi, dan karena dia mengintip internal mungkin sulit untuk menyusun kembali salinan string.

Tanggapan Mehrdad salah karena menyesatkan pada tingkat konseptual. Anda masih memiliki daftar byte, yang disandikan. Solusi khususnyanya memungkinkan pengganti yang tidak berpasangan untuk dilestarikan - ini tergantung pada implementasi. Solusi khususnya tidak akan menghasilkan byte string secara akurat jika GetBytesmengembalikan string dalam UTF-8 secara default.


Saya berubah pikiran tentang ini (solusi Mehrdad) - ini tidak mendapatkan byte dari string; melainkan mendapatkan byte dari array karakter yang dibuat dari string. Terlepas dari pengodean, char datatype di c # adalah ukuran tetap. Ini memungkinkan array byte panjang yang konsisten untuk diproduksi, dan memungkinkan array karakter direproduksi berdasarkan ukuran array byte. Jadi jika pengkodeannya adalah UTF-8, tetapi masing-masing karakter berukuran 6 byte untuk mengakomodasi nilai utf8 terbesar, itu masih akan berfungsi. Jadi memang - pengkodean karakter tidak masalah.

Tetapi konversi digunakan - setiap karakter ditempatkan ke dalam kotak ukuran tetap (tipe karakter c #). Namun, apa representasi itu tidak penting, yang secara teknis merupakan jawaban OP. Jadi - jika Anda tetap ingin mengonversi ... Kenapa tidak 'menyandikan'?

Gerard ONeill
sumber
Karakter-karakter ini tidak didukung oleh UTF-8 atau UTF-16 atau bahkan UTF-32 untuk exapmle: 񩱠& (Char) 55906& (Char) 55655. Jadi Anda mungkin salah dan jawaban Mehrdad adalah konversi yang aman tanpa mempertimbangkan jenis pengkodean apa yang digunakan.
Mojtaba Rezaeian
Raymon, karakter sudah diwakili oleh beberapa nilai unicode - dan semua nilai unicode dapat diwakili oleh semua utf. Apakah ada penjelasan yang lebih panjang tentang apa yang Anda bicarakan? Pengkodean karakter apa yang ada di dua nilai (atau 3 ..)?
Gerard ONeill
Mereka adalah karakter yang tidak valid yang tidak didukung oleh rentang pengkodean apa pun. Ini tidak berarti mereka 100% tidak berguna. Kode yang mengubah semua jenis string ke byte array yang setara terlepas dari pengkodean bukanlah solusi yang salah sama sekali dan memiliki penggunaannya sendiri pada kesempatan yang diinginkan.
Mojtaba Rezaeian
1
Ok, kalau begitu saya pikir Anda tidak mengerti masalahnya. Kita tahu itu adalah array yang sesuai unicode - pada kenyataannya, karena .net, kita tahu itu adalah UTF-16. Jadi karakter-karakter itu tidak akan ada di sana. Anda juga tidak sepenuhnya membaca komentar saya tentang perubahan representasi internal. String adalah objek, bukan array byte yang disandikan. Jadi saya tidak akan setuju dengan pernyataan terakhir Anda. Anda ingin kode untuk mengkonversi semua string unicode ke pengkodean UTF. Ini melakukan apa yang Anda inginkan, dengan benar.
Gerard ONeill
Objek adalah urutan data yang awalnya urutan bit yang menggambarkan objek dalam keadaan saat ini. Jadi setiap data dalam bahasa pemrograman dapat dikonversi ke array byte (setiap byte mendefinisikan 8 bit) karena Anda mungkin perlu menyimpan beberapa keadaan objek di memori. Anda dapat menyimpan dan menahan urutan byte dalam file atau memori dan melemparkannya sebagai integer, bigint, gambar, string Ascii, string UTF-8, string terenkripsi, atau tipe data yang Anda tentukan sendiri setelah membacanya dari disk. Jadi Anda tidak bisa mengatakan objek adalah sesuatu yang berbeda dari urutan byte.
Mojtaba Rezaeian
6

Anda dapat menggunakan kode berikut untuk mengonversi a stringmenjadi byte array.NET

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
Shyam sundar shah
sumber
3

Jika Anda benar-benar menginginkan salinan byte yang mendasari string, Anda dapat menggunakan fungsi seperti yang berikut. Namun, Anda sebaiknya tidak membaca terus untuk mengetahui alasannya.

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

Fungsi ini akan memberi Anda salinan byte yang mendasari string Anda, cukup cepat. Anda akan mendapatkan byte-byte itu dengan cara apa pun mereka meng-encode pada sistem Anda. Pengkodean ini hampir pasti UTF-16LE tetapi itu adalah detail implementasi yang tidak perlu Anda pedulikan.

Akan lebih aman, lebih sederhana dan lebih dapat diandalkan untuk hanya menelepon,

System.Text.Encoding.Unicode.GetBytes()

Kemungkinan ini akan memberikan hasil yang sama, lebih mudah untuk mengetik, dan byte akan selalu pulang pergi dengan panggilan ke

System.Text.Encoding.Unicode.GetString()
Jodrell
sumber
3

Berikut ini adalah implementasi tidak aman saya Stringuntuk Byte[]konversi:

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

Ini jauh lebih cepat daripada yang diterima, bahkan jika tidak seanggun itu. Berikut adalah tolok ukur Stopwatch saya lebih dari 10.000000 iterasi:

[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

Untuk menggunakannya, Anda harus mencentang "Izinkan Kode Tidak Aman" di properti build proyek Anda. Sesuai .NET Framework 3.5, metode ini juga dapat digunakan sebagai ekstensi String:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
Tommaso Belluzzo
sumber
Apakah nilai RuntimeHelpers.OffsetToStringDatakelipatan 8 pada versi Itanium dari .NET? Karena kalau tidak, ini akan gagal karena bacaan yang tidak selaras.
Jon Hanna
bukankah lebih mudah untuk memohon memcpy? stackoverflow.com/a/27124232/659190
Jodrell
2

Cukup gunakan ini:

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
alireza amini
sumber
2
... dan kehilangan semua karakter dengan lompatan mengatasi lebih tinggi dari 127. Dalam bahasa ibu saya, sangat valid untuk menulis "vrvíztűrő tükörfúrógép.". System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();akan mengembalikan "Árvizturo tukörfurogép."informasi yang hilang yang tidak dapat diambil. (Dan saya belum menyebutkan bahasa asia di mana Anda akan kehilangan semua karakter.)
mg30rg
2

String dapat dikonversi ke byte array dalam beberapa cara berbeda, karena fakta berikut: .NET mendukung Unicode, dan Unicode menstandarisasi beberapa pengkodean perbedaan yang disebut UTF. Mereka memiliki panjang representasi byte yang berbeda tetapi setara dalam arti bahwa ketika string dikodekan, dapat dikodekan kembali ke string, tetapi jika string dikodekan dengan satu UTF dan didekodekan dengan asumsi UTF berbeda jika dapat dikacaukan naik.

Juga, .NET mendukung pengkodean non-Unicode, tetapi mereka tidak berlaku dalam kasus umum (akan valid hanya jika sub-set terbatas titik kode Unicode digunakan dalam string aktual, seperti ASCII). Secara internal, .NET mendukung UTF-16, tetapi untuk representasi aliran, UTF-8 biasanya digunakan. Ini juga merupakan standar-de-facto untuk Internet.

Tidak mengherankan, serialisasi string ke dalam array byte dan deserialization didukung oleh kelas System.Text.Encoding, yang merupakan kelas abstrak; kelas turunannya mendukung pengkodean konkret: ASCIIEncodingdan empat UTF ( System.Text.UnicodeEncodingmendukung UTF-16)

Ref tautan ini.

Untuk serialisasi ke array byte yang menggunakan System.Text.Encoding.GetBytes. Untuk penggunaan operasi terbalik System.Text.Encoding.GetChars. Fungsi ini mengembalikan array karakter, jadi untuk mendapatkan string, gunakan konstruktor string System.String(char[]).
Ref halaman ini.

Contoh:

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? :-)
Vijay Singh Rana
sumber
2

Tergantung pada apa yang Anda inginkan untuk byte

Hal ini karena, sebagai Tyler sehingga tepat mengatakan , "Strings tidak data yang murni. Mereka juga memiliki informasi ." Dalam hal ini, informasi adalah pengkodean yang diasumsikan ketika string dibuat.

Dengan asumsi bahwa Anda memiliki data biner (bukan teks) yang disimpan dalam sebuah string

Ini didasarkan dari komentar OP pada pertanyaannya sendiri, dan merupakan pertanyaan yang tepat jika saya memahami petunjuk OP pada use-case.

Menyimpan data biner dalam string mungkin merupakan pendekatan yang salah karena asumsi pengkodean yang disebutkan di atas! Program atau pustaka apa pun yang menyimpan data biner dalam string(alih-alih byte[]array yang lebih cocok) telah kalah dalam pertarungan sebelum dimulai. Jika mereka mengirim byte kepada Anda dalam permintaan / tanggapan REST atau apa pun yang harus mengirimkan string, Base64 akan menjadi pendekatan yang tepat.

Jika Anda memiliki string teks dengan pengkodean yang tidak dikenal

Semua orang menjawab pertanyaan yang salah ini dengan tidak benar.

Jika string terlihat bagus apa adanya, pilih saja suatu pengkodean (lebih disukai yang dimulai dengan UTF), gunakan System.Text.Encoding.???.GetBytes()fungsi yang sesuai , dan beri tahu siapa pun yang Anda berikan byte pada pengodean yang Anda pilih.

NH.
sumber
2

Setelah ditanya apa yang ingin Anda lakukan dengan byte, Anda merespons :

Saya akan mengenkripsi itu. Saya dapat mengenkripsi tanpa mengubah tetapi saya masih ingin tahu mengapa encoding datang untuk bermain di sini. Beri saya byte adalah apa yang saya katakan.

Terlepas dari apakah Anda bermaksud mengirim data terenkripsi ini melalui jaringan, memuatnya kembali ke memori nanti, atau mengukusnya ke proses lain, Anda jelas bermaksud mendekripsi data itu di beberapa titik. Dalam hal ini, jawabannya adalah Anda mendefinisikan protokol komunikasi. Protokol komunikasi tidak boleh didefinisikan dalam hal rincian implementasi bahasa pemrograman Anda dan runtime terkait. Ada beberapa alasan untuk ini:

  • Anda mungkin perlu berkomunikasi dengan proses yang diimplementasikan dalam bahasa atau runtime yang berbeda. (Ini mungkin termasuk server yang berjalan di komputer lain atau mengirim string ke klien browser JavaScript, misalnya.)
  • Program ini dapat diimplementasikan kembali dalam bahasa yang berbeda atau runtime di masa depan.
  • Implementasi .NET dapat mengubah representasi internal string. Anda mungkin berpikir ini terdengar tidak masuk akal, tetapi ini sebenarnya terjadi di Java 9 untuk mengurangi penggunaan memori. Tidak ada alasan .NET tidak dapat mengikutinya. Skeet menunjukkan bahwa UTF-16 mungkin tidak optimal hari ini memberikan emoji dan blok Unicode lain yang membutuhkan lebih dari 2 byte untuk mewakili juga, meningkatkan kemungkinan bahwa representasi internal dapat berubah di masa depan.

Untuk berkomunikasi (baik dengan proses yang sama sekali berbeda atau dengan program yang sama di masa mendatang), Anda perlu mendefinisikan protokol Anda secara ketat untuk meminimalkan kesulitan bekerja dengannya atau secara tidak sengaja membuat bug. Bergantung pada representasi internal .NET bukanlah definisi yang ketat, jelas, atau bahkan dijamin konsisten. Pengkodean standar adalah definisi ketat yang tidak akan mengecewakan Anda di masa mendatang.

Dengan kata lain, Anda tidak dapat memenuhi persyaratan Anda untuk konsistensi tanpa menentukan pengkodean.

Anda tentu dapat memilih untuk menggunakan UTF-16 secara langsung jika Anda menemukan bahwa proses Anda berkinerja lebih baik sejak. NET menggunakannya secara internal atau karena alasan lain, tetapi Anda harus memilih pengkodean secara eksplisit dan melakukan konversi tersebut secara eksplisit dalam kode Anda daripada tergantung pada implementasi internal .NET.

Jadi pilih pengodean dan gunakan:

using System.Text;

// ...

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

Seperti yang Anda lihat, sebenarnya juga lebih sedikit kode untuk hanya menggunakan objek enkode bawaan daripada menerapkan metode pembaca / penulis Anda sendiri.

jpmc26
sumber
1

Dua arah:

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

Dan,

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

Saya cenderung menggunakan bagian bawah lebih sering daripada bagian atas, belum membandingkan mereka untuk kecepatan.


sumber
4
Bagaimana dengan karakter multibyte?
Agnel Kurian
c.ToByte () bersifat pribadi: S
Khodor
@AgnelKurian Msdn mengatakan "Metode ini mengembalikan nilai byte yang tidak ditandai yang mewakili kode numerik dari objek Char yang diteruskan. Dalam Framework .NET, objek Char adalah nilai 16-bit. Ini berarti bahwa metode ini cocok untuk mengembalikan kode numerik karakter dalam rentang karakter ASCII atau dalam Kontrol Unicode C0 dan Latin Dasar, dan rentang Kontrol C1 dan Suplemen Latin-1, dari U + 0000 hingga 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
pengguna1120193
sumber