ฉันจะได้รับการแสดงไบต์ที่สอดคล้องกันของสตริงใน C # โดยไม่ต้องระบุการเข้ารหัสด้วยตนเองได้อย่างไร


2189

ฉันจะแปลง a stringเป็นbyte[]in. NET (C #) โดยไม่ต้องระบุการเข้ารหัสเฉพาะด้วยตนเองได้อย่างไร

ฉันจะเข้ารหัสสตริง ฉันสามารถเข้ารหัสได้โดยไม่แปลง แต่ฉันก็ยังอยากรู้ว่าทำไมการเข้ารหัสจึงมาเล่นที่นี่

นอกจากนี้ทำไมการเข้ารหัสจึงควรนำมาพิจารณาด้วย ฉันไม่สามารถรับสตริงที่จัดเก็บไว้ในไบต์ใด ทำไมถึงต้องพึ่งพาการเข้ารหัสตัวอักษร?


23
ทุกสายถูกเก็บไว้เป็นอาร์เรย์ของไบต์ใช่ไหม? เหตุใดฉันจึงไม่มีไบต์เหล่านั้นไม่ได้
Agnel Kurian

135
การเข้ารหัสเป็นสิ่งที่แมปอักขระกับไบต์ ตัวอย่างเช่นใน ASCII ตัวอักษร 'A' จะจับคู่กับตัวเลข 65 ในการเข้ารหัสที่แตกต่างกันมันอาจไม่เหมือนกัน วิธีการระดับสูงกับสตริงที่ใช้ในกรอบงาน. NET ทำให้สิ่งนี้ไม่เกี่ยวข้องอย่างมากแม้ว่า (ยกเว้นในกรณีนี้)
Lucas Jones

20
หากต้องการเล่นทนายของปีศาจ: หากคุณต้องการรับไบต์ของสตริงในหน่วยความจำ (ขณะที่. NET ใช้แล้ว) และจัดการกับมันอย่างใดอย่างหนึ่ง (เช่น CRC32) และไม่เคยต้องการที่จะถอดรหัสกลับเป็นสตริงเดิม ... ไม่ได้ส่งตรงไปยังสาเหตุที่คุณสนใจการเข้ารหัสหรือวิธีที่คุณเลือกใช้
Greg

78
ยังไม่มีใครประหลาดใจที่ได้รับลิงก์นี้: joelonsoftware.com/articles/Unicode.html
Bevan

28
ถ่านไม่ใช่ไบต์และไบต์ไม่ใช่ถ่าน ถ่านเป็นกุญแจสำคัญในตารางแบบอักษรและประเพณีศัพท์ สตริงเป็นลำดับของตัวอักษร (คำ, ย่อหน้า, ประโยค, และชื่อเรื่องต่างก็มีขนบธรรมเนียมประเพณีศัพท์ของตนเองที่แสดงถึงคำจำกัดความประเภทของตัวเอง - แต่ฉันพูดนอกเรื่อง) เช่นเดียวกับจำนวนเต็มตัวเลขจุดลอยตัวและทุกอย่างอื่นตัวอักษรจะถูกเข้ารหัสเป็นไบต์ มีเวลาที่การเข้ารหัสง่าย ๆ แบบหนึ่งต่อหนึ่ง: 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: อย่างที่ฉันบอกจุดรวมของสิ่งนี้คือถ้าคุณต้องการที่จะใช้มันในระบบชนิดเดียวกันโดยมีฟังก์ชั่นชุดเดียวกัน ถ้าไม่เช่นนั้นคุณไม่ควรใช้มัน
user541686

193
-1 ฉันรับประกันได้ว่าใครบางคน (ที่ไม่เข้าใจตัวอักษรไบต์เทียบกับ) จะต้องการแปลงสตริงของพวกเขาเป็นอาร์เรย์ไบต์พวกเขาจะ google และอ่านคำตอบนี้และพวกเขาจะทำสิ่งผิดเพราะเกือบทั้งหมด กรณีการเข้ารหัสISที่เกี่ยวข้อง
artbristol

401
@artbristol: ถ้าพวกเขาไม่สามารถใส่ใจที่จะอ่านคำตอบ (หรือคำตอบอื่น ๆ ... ) แล้วฉันขอโทษแล้วไม่มีวิธีที่ดีกว่าสำหรับฉันที่จะสื่อสารกับพวกเขา โดยทั่วไปฉันเลือกที่จะตอบ OP แทนที่จะพยายามเดาว่าคนอื่นจะทำอะไรกับคำตอบของฉัน - OP มีสิทธิ์ที่จะรู้และเพียงเพราะใครบางคนอาจใช้มีดทำผิดกฎเกี่ยวไม่ได้หมายความว่าเราต้องซ่อนมีดทั้งหมดในโลก เพื่อตัวเราเอง แม้ว่าคุณจะไม่เห็นด้วยก็ไม่เป็นไร
user541686

185
คำตอบนี้ผิดในหลาย ๆ ระดับ แต่สำคัญที่สุดเพราะมันเป็นการประกาศ "คุณไม่จำเป็นต้องกังวลเกี่ยวกับการเข้ารหัส!" 2 เมธอด, GetBytes และ GetString มีความฟุ่มเฟือยมากพอ ๆ กับที่พวกเขาเป็นเพียงการนำไปใช้ใหม่ของสิ่งที่ Encoding.Unicode.GetBytes () และ Encoding.Unicode.GetString () ทำอยู่แล้ว คำสั่ง "ตราบใดที่โปรแกรมของคุณ (หรือโปรแกรมอื่น ๆ ) อย่าพยายามตีความ bytes" ก็มีข้อบกพร่องพื้นฐานเช่นกันโดยปริยายพวกเขาหมายถึงไบต์ควรถูกตีความเป็น Unicode
David

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 UTF-16เพื่อเป็นตัวแทนของสายดังนั้นหากคุณเพียงแค่ต้องการที่จะได้รับไบต์ที่แน่นอนที่ใช้ .NET System.Text.Encoding.Unicode.GetBytes (...)ใช้

ดูการเข้ารหัสอักขระใน. NET Framework (MSDN) สำหรับข้อมูลเพิ่มเติม


14
แต่ทำไมการเข้ารหัสควรนำมาพิจารณาด้วย เหตุใดฉันจึงไม่สามารถรับไบต์โดยไม่ต้องดูการเข้ารหัสที่ใช้อยู่ แม้ว่ามันจะถูกต้องการวัตถุ String ไม่ควรรู้ว่าการเข้ารหัสที่ใช้และเพียงแค่ทิ้งสิ่งที่อยู่ในหน่วยความจำ?
Agnel Kurian

57
สตริง. NET จะถูกเข้ารหัสเป็น Unicode เสมอ ดังนั้นให้ใช้ System.Text.Encoding.Unicode.GetBytes (); เพื่อรับชุดไบต์ที่. NET จะใช้เพื่อเป็นตัวแทนของตัวละคร อย่างไรก็ตามทำไมคุณต้องการที่ ฉันแนะนำ UTF-8 โดยเฉพาะอย่างยิ่งเมื่อตัวละครส่วนใหญ่อยู่ในชุดลาตินตะวันตก
AnthonyWJones

8
นอกจากนี้: ไบต์ที่แน่นอนที่ใช้ภายในในสตริงไม่สำคัญว่าระบบที่ดึงข้อมูลนั้นจะไม่จัดการการเข้ารหัสนั้นหรือจัดการเป็นการเข้ารหัสที่ไม่ถูกต้อง ถ้ามันทั้งหมดอยู่ใน. Net ทำไมแปลงเป็นอาร์เรย์ไบต์เลย มิฉะนั้นจะเป็นการดีกว่าหากคุณมีการเข้ารหัสอย่างชัดเจน
Joel Coehoorn

11
@ Joel ระวังด้วย System.Text.Encoding.Default เนื่องจากอาจแตกต่างกันไปในแต่ละเครื่องที่รันอยู่ นี่คือสาเหตุที่แนะนำให้ระบุการเข้ารหัสเสมอเช่น UTF-8
Ash

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
ในกรณีที่คำตอบที่ได้รับการยอมรับมีการเปลี่ยนแปลงเพื่อวัตถุประสงค์ในการบันทึกมันเป็นคำตอบของ Mehrdad ณ เวลาและวันที่ปัจจุบัน หวังว่า OP จะกลับมาทบทวนเรื่องนี้และยอมรับทางออกที่ดีกว่า
Thomas Eding

7
หลักการดี แต่การเข้ารหัสควรSystem.Text.Encoding.Unicodeจะเท่ากับคำตอบของ Mehrdad
Jodrell

5
คำถามได้ถูกแก้ไขไปแล้วนับครั้งไม่ถ้วนนับตั้งแต่คำตอบดั้งเดิมดังนั้นบางทีคำตอบของฉันอาจล้าสมัยไปนิด ฉันไม่เคยตั้งใจจะให้ exace เทียบเท่ากับคำตอบของ Mehrdad แต่ให้วิธีที่เหมาะสมในการทำมัน แต่คุณอาจพูดถูก อย่างไรก็ตามวลี "ได้รับสิ่งที่ไบต์สตริงที่ถูกเก็บไว้ใน" ในคำถามเดิมจะไม่ถูกต้องมาก เก็บไว้ที่ไหน ในความทรงจำ? บนดิสก์? หากอยู่ในหน่วยความจำSystem.Text.Encoding.Unicode.GetBytesอาจจะแม่นยำกว่านี้
Erik A. Brandstadmoen

7
@AMissico ข้อเสนอแนะของคุณมีข้อยกเว้นถ้าคุณแน่ใจว่าสตริงของคุณเข้ากันได้กับการเข้ารหัสเริ่มต้นระบบของคุณ (สตริงที่มีเฉพาะอักขระ ASCII ในชุดอักขระเริ่มต้นระบบดั้งเดิมของคุณ) แต่ไม่มีที่ไหนเลยที่ OP กล่าว
Frédéric

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) และการเข้ารหัสที่แตกต่างกันจะถือว่าไบต์เหล่านี้แตกต่างกัน

Joel ได้โพสต์ข้อความนี้:

ขั้นต่ำที่แน่นอนนักพัฒนาซอฟต์แวร์ทุกคนอย่างแน่นอนบวกต้องทราบเกี่ยวกับ Unicode และชุดอักขระ (ไม่มีข้อแก้ตัว!)


6
"1 อักขระสามารถแทนได้ 1 ไบต์ขึ้นไป" ฉันเห็นด้วย ฉันแค่ต้องการไบต์เหล่านั้นโดยไม่คำนึงถึงสิ่งที่เข้ารหัสสตริงเป็นวิธีเดียวที่สตริงสามารถเก็บไว้ในหน่วยความจำเป็นไบต์ แม้กระทั่งตัวละครจะถูกเก็บไว้อย่างน้อย 1 ไบต์ ฉันแค่อยากได้รับไบต์ของพวกเขา
Agnel Kurian

16
คุณไม่จำเป็นต้องเข้ารหัสเว้นแต่ว่าคุณ (หรือคนอื่น ๆ ) ตั้งใจที่จะตีความข้อมูลแทนการถือเป็น "บล็อกไบต์" ทั่วไป สำหรับสิ่งต่างๆเช่นการบีบอัดการเข้ารหัส ฯลฯ การกังวลเกี่ยวกับการเข้ารหัสนั้นไม่มีความหมาย ดูคำตอบของฉันสำหรับวิธีการทำเช่นนี้โดยไม่ต้องกังวลกับการเข้ารหัส
user541686

9
@ Mehrdad - ทั้งหมด แต่คำถามเดิมตามที่ระบุไว้ในตอนแรกที่ฉันตอบไม่ได้ว่าสิ่งที่ OP จะเกิดขึ้นกับไบต์เหล่านั้นหลังจากที่พวกเขาได้แปลงพวกเขาและสำหรับผู้ค้นหาในอนาคตข้อมูลรอบที่เกี่ยวข้อง - นี่คือ ครอบคลุมโดยคำตอบของ Joelค่อนข้างดี - และเมื่อคุณระบุไว้ในคำตอบ: หากคุณติดอยู่ในโลกของ. NET และใช้วิธีการของคุณในการแปลงเป็น / จากคุณมีความสุข ทันทีที่คุณก้าวออกไปข้างนอกการเข้ารหัสจะมีผล
Zhaph - Ben Duguid

จุดรหัสหนึ่งจุดสามารถแทนได้สูงสุด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.UnicodeEncoding.UTF16

สำหรับประเพณีส่วนใหญ่ ถ้านั่นคือสิ่งที่คุณต้องการหยุดอ่านที่นี่ ดูบทความ Joel Spolsky ที่สนุกถ้าคุณไม่เข้าใจว่าการเข้ารหัสคืออะไร

ความต้องการเฉพาะ

ตอนนี้ผู้เขียนคำถามถามว่า "ทุกสตริงถูกเก็บไว้เป็นอาร์เรย์ไบต์ใช่มั้ยทำไมฉันถึงไม่มีไบต์เหล่านั้นไม่ได้"

เขาไม่ต้องการการแปลงใด ๆ

จากข้อกำหนดC # :

การประมวลผลอักขระและสตริงใน C # ใช้การเข้ารหัส Unicode ชนิดถ่านแสดงถึงหน่วยรหัส UTF-16 และชนิดสตริงแสดงถึงลำดับของหน่วยรหัส UTF-16

ดังนั้นเรารู้ว่าถ้าเราขอการแปลงค่าว่าง (เช่นจาก UTF-16 ถึง UTF-16) เราจะได้ผลลัพธ์ที่ต้องการ:

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

แต่เพื่อหลีกเลี่ยงการกล่าวถึงการเข้ารหัสเราต้องทำอีกวิธีหนึ่ง หากยอมรับชนิดข้อมูลระดับกลางจะมีทางลัดทางความคิดสำหรับสิ่งนี้:

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

ที่ไม่ได้รับเราประเภทข้อมูลที่ต้องการ แต่Mehrdad ของคำตอบที่แสดงให้เห็นถึงวิธีการแปลงนี้แถวถ่านเป็นแถวไบต์ใช้BlockCopy อย่างไรก็ตามสิ่งนี้จะคัดลอกสตริงสองครั้ง! และมันชัดเจนเกินไปจะใช้รหัสการเข้ารหัสเฉพาะ: System.Charประเภทข้อมูล

วิธีเดียวที่จะไปถึงจำนวนไบต์จริงที่เก็บสตริงไว้คือใช้ตัวชี้ fixedคำสั่งช่วยให้การอยู่ค่า จากข้อกำหนด C #:

[สำหรับ] การแสดงออกของสตริงประเภท ... initializer คำนวณที่อยู่ของตัวอักษรตัวแรกในสตริง

ต้องการทำเช่นนั้นเรียบเรียงเขียนรหัสเฮี๊ยบกว่าส่วนอื่น ๆ 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 ชี้ให้เห็นผลลัพธ์ขึ้นอยู่กับ endianness ของเครื่อง แต่ผู้เขียนคำถามไม่ได้เกี่ยวข้องกับเรื่องนั้น


3
@Jan นั้นถูกต้อง แต่ความยาวของสตริงจะให้จำนวนรหัสหน่วย (ไม่ใช่รหัสสะสม)
Tom Blodget

1
ขอบคุณสำหรับการชี้ให้เห็น! จาก MSDN: " Lengthคุณสมบัติ [ของString] ส่งคืนจำนวนCharวัตถุในอินสแตนซ์นี้ไม่ใช่จำนวนอักขระ Unicode" รหัสตัวอย่างของคุณจึงถูกต้องตามที่เขียนไว้
Jan Hettich

1
@supercat "ประเภทถ่านแสดงถึงรหัสหน่วย UTF-16 และประเภทสตริงแสดงถึงลำดับของหน่วยรหัส UTF-16" —_ C # 5 ข้อมูลจำเพาะ _ แม้ว่าใช่ไม่มีสิ่งใดที่ป้องกันสตริง Unicode ที่ไม่ถูกต้อง:new String(new []{'\uD800', '\u0030'})
Tom Blodget

1
@ TomBlodget: ที่น่าสนใจถ้ามีอินสแตนซ์ของGlobalization.SortKeyแยกKeyDataและแพ็คไบต์ที่ได้จากแต่ละอันลงในString[สองไบต์ต่อตัวอักษรMSB แรก ] การเรียกString.CompareOrdinalใช้สตริงผลลัพธ์จะเร็วกว่าการโทรหาSortKey.Compareอินสแตนซ์ของSortKeyหรือ แม้กระทั่งโทรหาmemcmpอินสแตนซ์เหล่านั้น ระบุว่าฉันสงสัยว่าทำไมKeyDataผลตอบแทนByte[]มากกว่าString?
supercat

1
อนิจจาคำตอบที่ถูกต้อง แต่หลายปีที่ผ่านมาจะไม่มีการลงคะแนนมากเท่าที่ยอมรับ เนื่องจาก TL ผู้คน DR จะคิดว่าหินคำตอบที่ยอมรับได้ copyenpastit และขึ้นคะแนนมัน
Martin Capodici

46

ส่วนแรกของคำถามของคุณ (วิธีรับไบต์) ได้รับคำตอบจากผู้อื่นแล้ว: ดูในSystem.Text.Encodingเนมสเปซ

ฉันจะตอบคำถามที่ตามมาของคุณ: ทำไมคุณต้องเลือกการเข้ารหัส ทำไมคุณไม่ได้รับสิ่งนั้นจากคลาสสตริงเอง?

คำตอบนั้นเป็นสองส่วน

ก่อนอื่นไบต์ที่ใช้ภายในโดยคลาสสตริงนั้นไม่สำคัญและเมื่อใดก็ตามที่คุณคิดว่าพวกเขาทำคุณจะมีโอกาสแนะนำบั๊ก

หากโปรแกรมของคุณอยู่ในโลกของ. Net คุณไม่จำเป็นต้องกังวลกับการใช้ไบต์อาร์เรย์สำหรับสตริงเลยแม้ว่าคุณจะส่งข้อมูลผ่านเครือข่ายก็ตาม ให้ใช้. Net Serialization แทนเพื่อกังวลเกี่ยวกับการส่งข้อมูล คุณไม่ต้องกังวลกับไบต์ที่แท้จริงอีกต่อไป: ตัวจัดรูปแบบการทำให้เป็นอนุกรมจะทำเพื่อคุณ

ในทางกลับกันถ้าคุณส่งไบต์เหล่านี้ไปที่ไหนสักแห่งที่คุณไม่สามารถรับประกันได้ว่าจะดึงข้อมูลจากกระแสข้อมูลแบบอนุกรม. Net ในกรณีนี้คุณจำเป็นต้องกังวลเกี่ยวกับการเข้ารหัสเพราะแน่นอนว่าระบบภายนอกนี้ใส่ใจ ดังนั้นอีกครั้งไบต์ภายในที่ใช้โดยสตริงไม่สำคัญ: คุณต้องเลือกการเข้ารหัสเพื่อให้คุณสามารถระบุได้อย่างชัดเจนเกี่ยวกับการเข้ารหัสนี้เมื่อสิ้นสุดการรับแม้ว่าจะเป็นการเข้ารหัสแบบเดียวกันที่ใช้ภายในโดย. Net

ฉันเข้าใจว่าในกรณีนี้คุณอาจต้องการใช้ไบต์จริงที่เก็บไว้โดยตัวแปรสตริงในหน่วยความจำเท่าที่จะเป็นไปได้ด้วยแนวคิดที่ว่าอาจบันทึกงานที่สร้างกระแสข้อมูลไบต์ของคุณ อย่างไรก็ตามฉันคิดกับคุณว่ามันไม่สำคัญเมื่อเทียบกับการทำให้แน่ใจว่าผลลัพธ์ของคุณจะถูกเข้าใจที่ปลายอีกด้านและเพื่อรับประกันว่าคุณจะต้องชัดเจนด้วยการเข้ารหัสของคุณ นอกจากนี้หากคุณต้องการจับคู่ไบต์ภายในของคุณจริงๆคุณสามารถเลือกการUnicodeเข้ารหัสและประหยัดประสิทธิภาพได้แล้ว

ซึ่งนำฉันไปยังส่วนที่สอง ... การเลือกการUnicodeเข้ารหัสกำลังบอก. Net ให้ใช้ไบต์พื้นฐาน คุณจำเป็นต้องเลือกการเข้ารหัสนี้เพราะเมื่อ Unicode-Plus แบบใหม่บางตัวออกมา. Net runtime จำเป็นต้องมีอิสระในการใช้รูปแบบการเข้ารหัสที่ใหม่และดีกว่านี้โดยไม่ทำให้โปรแกรมของคุณพัง แต่ในขณะนี้ (และในอนาคตอันใกล้) เพียงแค่เลือกการเข้ารหัส Unicode จะให้สิ่งที่คุณต้องการ

นอกจากนี้ยังเป็นสิ่งสำคัญที่จะเข้าใจสายของคุณจะต้องมีการเขียนใหม่ลวดและที่เกี่ยวข้องอย่างน้อยการแปลของบิตรูปแบบบางอย่างแม้เมื่อคุณใช้การเข้ารหัสที่ตรงกัน คอมพิวเตอร์ต้องคำนึงถึงสิ่งต่าง ๆ เช่น Big vs Little Endian ลำดับไบต์ของเครือข่ายการแพ็คเก็ตข้อมูลเซสชัน ฯลฯ


9
มีพื้นที่ใน. NET ที่คุณต้องได้รับอาร์เรย์ไบต์สำหรับสตริง คลาส Cryptrography .NET จำนวนมากมีวิธีการเช่น ComputeHash () ที่ยอมรับอาร์เรย์อาร์เรย์หรือสตรีม คุณไม่มีทางเลือกนอกจากการแปลงสตริงเป็นอาร์เรย์ไบต์ก่อน (เลือกการเข้ารหัส) จากนั้นเลือกที่จะตัดมันในสตรีม อย่างไรก็ตามตราบใดที่คุณเลือกการเข้ารหัส (เช่น UTF8) แท่งที่ติดอยู่จะไม่มีปัญหากับสิ่งนี้
Ash

44

เพียงเพื่อแสดงให้เห็นว่า Mehrdrad ของเสียงคำตอบผลงานวิธีการของเขายังสามารถยังคงมีอยู่อักขระตัวแทน unpaired (ซึ่งหลายคนจ่อกับคำตอบของฉัน แต่ที่ทุกคนอย่างเท่าเทียมกันความผิดของเช่น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

ทุกครั้งที่มีการเคลื่อนไหวในคำถามนี้ฉันยังคงคิดถึง serializer (ไม่ว่าจะเป็นจาก Microsoft หรือจากบุคคลที่สาม) ที่สามารถคงอยู่ในสตริงแม้จะมีอักขระตัวแทนที่ไม่มีคู่ ฉัน google นี้ทุกขณะนี้แล้ว: อนุกรมอักขระตัวแทน unpaired .NET นี่ไม่ได้ทำให้ฉันนอนไม่หลับ แต่มันก็น่ารำคาญเมื่อทุก ๆ ครั้งที่มีใครบางคนแสดงความคิดเห็นกับคำตอบของฉันว่ามันมีข้อบกพร่อง แต่คำตอบของพวกเขาก็มีข้อบกพร่องอย่างเท่าเทียมกัน

ถึงอย่างนั้น, Microsoft ควรใช้System.Buffer.BlockCopyมันในBinaryFormatter

谢谢!


3
ไม่ตัวแทนต้องปรากฏเป็นคู่เพื่อสร้างคะแนนรหัสที่ถูกต้อง? หากเป็นกรณีนี้ฉันสามารถเข้าใจได้ว่าทำไมข้อมูลจะถูกจัดการ
dtanders

1
@dtanders ใช่นั่นคือความคิดของฉันเช่นกันพวกเขาจะต้องปรากฏตัวเป็นคู่ตัวละครตัวแทนที่ไม่ได้รับการจับคู่เกิดขึ้นถ้าคุณจงใจใส่มันลงไปในสตริง สิ่งที่ฉันไม่ทราบคือเหตุผลที่ devs อื่น ๆ เก็บไว้ในพิณที่เราควรใช้วิธีการเข้ารหัสตระหนักแทนเพราะพวกเขาถือว่าเป็นวิธีการต่อเนื่อง ( คำตอบของฉันซึ่งเป็นคำตอบที่ได้รับการยอมรับมานานกว่า 3 ปี) ไม่ทำให้คู่ ตัวละครตัวแทนเหมือนเดิม แต่พวกเขาลืมที่จะตรวจสอบว่าวิธีการแก้ปัญหาการเข้ารหัสของพวกเขาไม่ได้รักษาตัวละครตัวแทนที่ไร้คู่เช่นกันประชด iron
Michael Buen

หากมีห้องสมุดที่เป็นอนุกรมที่ใช้System.Buffer.BlockCopyภายในข้อโต้แย้งของคนที่สนับสนุนการเข้ารหัสทั้งหมดจะถูกสงสัย
Michael Buen

2
@MichaelBuen ดูเหมือนว่าปัญหาหลักคือคุณเป็นตัวหนาตัวใหญ่ที่พูดอะไรบางอย่างไม่สำคัญแทนที่จะพูดว่ามันไม่สำคัญในกรณีของพวกเขา เป็นผลให้คุณสนับสนุนให้คนที่ดูคำตอบของคุณเพื่อทำผิดพลาดในการเขียนโปรแกรมขั้นพื้นฐานซึ่งจะทำให้คนอื่น ๆ แห้วในอนาคต ตัวแทนเสมือนที่ไม่ได้จับคู่นั้นไม่ถูกต้องในสตริง ไม่ใช่อาเรย์ถ่านดังนั้นจึงเหมาะสมที่การแปลงสตริงเป็นรูปแบบอื่นอาจส่งผลให้เกิดข้อผิดพลาดFFFDกับอักขระนั้น หากคุณต้องการทำการจัดการสตริงด้วยตนเองให้ใช้อักขระ [] ตามที่แนะนำ
Trisped

2
@dtanders: การ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 ความคิดเห็นที่ถูกต้องมากขึ้นที่นี่แม้ว่าจะเป็นสัญลักษณ์ unicode ที่เข้ารหัส (ดังนั้นเป็นไบต์) อักขระที่มีการกำกับของตนเองจะให้ผลลัพธ์ที่แตกต่างจากการกำกับแยกออกเป็นสัญลักษณ์ตัวแก้ไขที่เพิ่มเข้าไปในตัวอักขระ แต่ iirc มีวิธีการใน. net เพื่อแยกออกโดยเฉพาะเพื่อให้ได้รับการเป็นตัวแทนไบต์ที่สอดคล้องกัน
Nyerguds

25

ฉันได้อ่านคำตอบทั้งหมดและพวกเขากำลังใช้การเข้ารหัสหรืออย่างใดอย่างหนึ่งเกี่ยวกับการทำให้เป็นอนุกรมที่ลดตัวแทนเสมือน

มันไม่ดีเมื่อสตริงเช่นมาจากSQL Serverที่มันถูกสร้างขึ้นจากการจัดเก็บอาร์เรย์ไบต์เช่นแฮรหัสผ่าน หากเราทิ้งอะไรลงไปมันจะเก็บแฮชที่ไม่ถูกต้องและถ้าเราต้องการเก็บไว้ใน XML เราต้องการปล่อยมันไว้เหมือนเดิม (เพราะตัวเขียน XML จะทิ้งข้อยกเว้นในการค้นหาตัวแทนตัวแทนที่ไม่มีคู่)

ดังนั้นฉันจึงใช้การเข้ารหัสBase64ของอาร์เรย์ไบต์ในกรณีเช่นนี้ แต่เฮ้บนอินเทอร์เน็ตมีวิธีแก้ปัญหาเพียงวิธีเดียวใน C # และมีข้อผิดพลาดอยู่และเป็นวิธีเดียวดังนั้นฉันจึงแก้ไขข้อผิดพลาดและเขียนกลับ ขั้นตอน ที่นี่คุณคือชาว Google ในอนาคต:

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

@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)
Gman

23

โปรดอธิบายด้วยว่าเหตุใดการเข้ารหัสจึงควรนำมาพิจารณาด้วย ฉันไม่สามารถรับสตริงที่จัดเก็บไว้ในไบต์ใด ทำไมต้องพึ่งพาการเข้ารหัสนี้ !!!

เพราะไม่มีสิ่งเช่น "ไบต์ของสตริง"

สตริง (หรือโดยทั่วไปข้อความ) ประกอบด้วยอักขระ: ตัวอักษรตัวเลขและสัญลักษณ์อื่น ๆ นั่นคือทั้งหมดที่ คอมพิวเตอร์อย่างไรก็ตามไม่รู้อะไรเกี่ยวกับตัวละคร พวกเขาสามารถจัดการไบต์เท่านั้น ดังนั้นหากคุณต้องการจัดเก็บหรือส่งข้อความโดยใช้คอมพิวเตอร์คุณจะต้องแปลงอักขระเป็นไบต์ คุณจะทำอย่างไร ที่นี่การเข้ารหัสมาที่ฉาก

การเข้ารหัสคืออะไรนอกจากการประชุมเพื่อแปลอักขระตรรกะเป็นฟิสิคัลไบต์ การเข้ารหัสที่ง่ายและเป็นที่รู้จักมากที่สุดคือ ASCII และเป็นสิ่งที่คุณต้องการหากคุณเขียนเป็นภาษาอังกฤษ สำหรับภาษาอื่นคุณจะต้องมีการเข้ารหัสที่สมบูรณ์ยิ่งขึ้นการเป็น Unicode ใด ๆ เป็นทางเลือกที่ปลอดภัยที่สุดในปัจจุบัน

ดังนั้นในระยะสั้นพยายาม "รับไบต์ของสตริงโดยไม่ต้องใช้การเข้ารหัส" เป็นไปไม่ได้เช่นเดียวกับ "การเขียนข้อความโดยไม่ใช้ภาษาใด ๆ "

อย่างไรก็ตามฉันขอแนะนำให้คุณ (และทุกคนสำหรับเรื่องนี้) อ่านสติปัญญาชิ้นเล็ก ๆ : ขั้นต่ำสุดของนักพัฒนาซอฟต์แวร์ทุกคนอย่างแน่นอนแน่นอนต้องรู้เกี่ยวกับ Unicode และชุดอักขระ (ไม่มีข้อแก้ตัว!)


2
อนุญาตให้ฉันอธิบาย: มีการใช้การเข้ารหัสเพื่อแปล "สวัสดีโลก" เป็นฟิสิคัลไบต์ เนื่องจากสตริงถูกเก็บไว้ในคอมพิวเตอร์ของฉันฉันแน่ใจว่ามันจะต้องถูกเก็บไว้ในหน่วยไบต์ ฉันแค่ต้องการเข้าถึงไบต์เหล่านั้นเพื่อบันทึกไว้ในดิสก์หรือด้วยเหตุผลอื่น ฉันไม่ต้องการตีความไบต์เหล่านี้ เนื่องจากฉันไม่ต้องการตีความไบต์เหล่านี้ความต้องการการเข้ารหัส ณ จุดนี้จึงถูกวางผิดที่เนื่องจากต้องใช้สายโทรศัพท์เพื่อเรียก printf
Agnel Kurian

3
แต่อีกครั้งไม่มีแนวคิดของการแปลงข้อความเป็นฟิสิคัล - ไบต์ - การแปลเว้นแต่ว่าคุณจะใช้การเข้ารหัส แน่นอนว่าคอมไพเลอร์เก็บสตริงอย่างใดในหน่วยความจำ - แต่มันเป็นเพียงแค่ใช้การเข้ารหัสภายในซึ่งคุณ (หรือใครก็ตามยกเว้นผู้พัฒนาคอมไพเลอร์) ไม่ทราบ ดังนั้นไม่ว่าคุณจะทำอะไรคุณต้องมีการเข้ารหัสเพื่อรับฟิสิคัลไบต์จากสตริง
Konamiman

@Agnel Kurian: เป็นเรื่องจริงแน่นอนว่าสตริงมีจำนวนไบต์ที่หนึ่งที่เก็บเนื้อหา (UTF-16 ไกล) แต่มีเหตุผลที่ดีที่จะป้องกันไม่ให้คุณเข้าถึง: สตริงนั้นไม่เปลี่ยนรูปและถ้าคุณสามารถรับอาร์เรย์ไบต์ [] ภายในคุณสามารถปรับเปลี่ยนได้เช่นกัน สิ่งนี้จะทำลายความไม่สามารถเปลี่ยนแปลงได้ซึ่งมีความสำคัญเนื่องจากหลายสายอาจแชร์ข้อมูลเดียวกัน การใช้การเข้ารหัส UTF-16 เพื่อรับสายอาจจะเพียงแค่คัดลอกข้อมูลออกมา
ollb

2
@Gnafoo สำเนาของไบต์จะทำ
Agnel Kurian

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 ไม่ควรรู้ว่าการเข้ารหัสที่ใช้และเพียงแค่ทิ้งสิ่งที่อยู่ในหน่วยความจำ?
Agnel Kurian

5
มันไม่ได้ผลเสมอไป ตัวละครพิเศษบางตัวอาจหลงทางโดยใช้วิธีการที่ฉันได้พบวิธีที่ยาก
JB King

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 วิธีนี้แก้ไขปัญหาของฉัน (byte [] 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 answer คือ 14 ไบต์ในทั้งสองอินสแตนซ์ในขณะที่คำตอบ UTF-8 มีเพียง 9 ไบต์สำหรับครั้งแรกและเพียง 7 สำหรับวินาทีเท่านั้น

ดังนั้นหากคุณต้องการไบต์ที่ใช้โดยสตริงให้ใช้Encoding.Unicodeแต่มันจะไม่มีประสิทธิภาพกับพื้นที่เก็บข้อมูล


10

ปัญหาสำคัญคือ glyph ในสตริงใช้เวลา 32 บิต (16 บิตสำหรับรหัสอักขระ) แต่ไบต์มี 8 บิตเพื่อสำรอง ไม่มีการแมปแบบหนึ่งต่อหนึ่งยกเว้นว่าคุณ จำกัด ตัวเองเป็นสตริงที่มีอักขระ ASCII เท่านั้น System.Text.Encoding มีหลายวิธีในการแมปสตริงไปยังไบต์ [] คุณต้องเลือกอันที่หลีกเลี่ยงการสูญเสียข้อมูลและลูกค้าของคุณใช้งานได้ง่ายเมื่อเธอต้องการแมปไบต์ [] กลับไปที่สตริง .

Utf8 เป็นการเข้ารหัสที่ได้รับความนิยมมันมีขนาดกะทัดรัดและไม่สูญเสีย


3
UTF-8 มีขนาดกะทัดรัดก็ต่อเมื่อตัวละครส่วนใหญ่ของคุณอยู่ในชุดอักขระภาษาอังกฤษ (ASCII) หากคุณมีตัวอักษรจีนจำนวนมาก UTF-16 จะเป็นการเข้ารหัสที่กะทัดรัดกว่า UTF-8 สำหรับสตริงนั้น นี่เป็นเพราะ UTF-8 ใช้หนึ่งไบต์ในการเข้ารหัส ASCII และ 3 (หรืออาจจะ 4)
Joel Mueller

7
จริง แต่คุณจะไม่รู้การเข้ารหัสได้อย่างไรถ้าคุณคุ้นเคยกับการจัดการข้อความภาษาจีน
46919 Hans Passant

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 ขอให้เฉพาะเจาะจงที่จะไม่ระบุการเข้ารหัส ... "โดยไม่ต้องระบุการเข้ารหัสที่เฉพาะเจาะจง"
Ferdz

8

วิธีที่เร็วที่สุด

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

แก้ไข ตามที่ Makotosan แสดงความคิดเห็นว่านี่เป็นวิธีที่ดีที่สุด:

Encoding.UTF8.GetBytes(text)

8
ASCII ไม่จำเป็นต้องใช้การเข้ารหัส ..... เพียงต้องการใช้ Encoding.UTF8.GetBytes (ข้อความ)
Makotosan

8

ฉันจะแปลงสตริงเป็นไบต์ [] ใน. NET (C #) โดยไม่ต้องระบุการเข้ารหัสเฉพาะด้วยตนเองได้อย่างไร

สตริงใน .NET แสดงถึงข้อความที่เป็นลำดับของหน่วย UTF-16 รหัสดังนั้นไบต์ที่มีการเข้ารหัสในหน่วยความจำใน UTF-16 แล้ว

คำตอบของ Mehrdad

คุณสามารถใช้คำตอบของ Mehrdadแต่จริงๆแล้วมันใช้การเข้ารหัสเพราะตัวอักษรเป็น UTF-16 เรียก ToCharArray ซึ่งดูแหล่งที่มาสร้างchar[]และคัดลอกหน่วยความจำไปยังโดยตรง จากนั้นก็คัดลอกข้อมูลไปยังอาร์เรย์ไบต์ที่ถูกจัดสรรเช่นกัน ดังนั้นภายใต้ประทุนจะทำการคัดลอกไบต์พื้นฐานสองครั้งและจัดสรรอาร์เรย์ถ่านที่ไม่ได้ใช้หลังจากการโทร

คำตอบของ Tom Blodget

คำตอบของ Tom Blodgetเร็วกว่า Mehrdad 20-30% เนื่องจากมันข้ามขั้นตอนกลางของการจัดสรรอาร์เรย์ char และคัดลอกไบต์ไปยังมัน แต่คุณต้องรวบรวม/unsafeตัวเลือก หากคุณไม่ต้องการใช้การเข้ารหัสฉันคิดว่านี่เป็นวิธีที่จะไป หากคุณใส่การเข้ารหัสลับไว้ในfixedบล็อกคุณไม่จำเป็นต้องจัดสรรอาร์เรย์แยกต่างหากและคัดลอกไบต์ไป

นอกจากนี้ทำไมการเข้ารหัสควรนำมาพิจารณาด้วย ฉันไม่สามารถรับสตริงที่จัดเก็บไว้ในไบต์ใด ทำไมถึงต้องพึ่งพาการเข้ารหัสตัวอักษร?

เพราะนั่นเป็นวิธีที่เหมาะสมในการทำ stringเป็นสิ่งที่เป็นนามธรรม

การใช้การเข้ารหัสอาจทำให้คุณมีปัญหาหากคุณมี 'สายอักขระ' ที่มีอักขระที่ไม่ถูกต้อง แต่นั่นไม่ควรเกิดขึ้น หากคุณได้รับข้อมูลลงในสายอักขระที่มีอักขระที่ไม่ถูกต้องแสดงว่าคุณทำผิด คุณควรจะใช้อาร์เรย์ไบต์หรือการเข้ารหัส Base64 เพื่อเริ่มต้นด้วย

หากคุณใช้System.Text.Encoding.Unicodeรหัสของคุณจะยืดหยุ่นมากขึ้น คุณไม่ต้องกังวลเกี่ยวกับendiannessของระบบรหัสของคุณจะทำงานใน คุณไม่ต้องกังวลว่า CLR เวอร์ชั่นถัดไปจะใช้การเข้ารหัสอักขระภายในแบบอื่นหรือไม่

ฉันคิดว่าคำถามไม่ใช่สาเหตุที่คุณต้องการกังวลเกี่ยวกับการเข้ารหัส แต่ทำไมคุณถึงไม่สนใจมันและใช้อย่างอื่น การเข้ารหัสหมายถึงการแสดงสิ่งที่เป็นนามธรรมของสตริงในลำดับไบต์ System.Text.Encoding.Unicodeจะให้การเข้ารหัสคำสั่งไบต์ endian เล็กน้อยและจะดำเนินการเหมือนกันในทุกระบบในปัจจุบันและอนาคต


ที่จริงแล้วสตริงใน C # ไม่ได้ จำกัด เพียงแค่ UTF-16 สิ่งที่เป็นจริงคือมันมีเวกเตอร์ของหน่วยรหัส 16 บิต แต่หน่วยรหัส 16 บิตเหล่านี้ไม่ได้ถูก จำกัด ให้ใช้ UTF-16 ที่ถูกต้อง แต่เนื่องจากเป็น 16 บิตคุณต้องมีการเข้ารหัส (ลำดับไบต์) เพื่อแปลงเป็น 8 บิต สตริงสามารถจัดเก็บข้อมูลที่ไม่ใช่ Unicode รวมถึงรหัสไบนารี่ (เช่นรูปภาพบิตแมป) มันจะตีความเป็น UTF-16 เฉพาะใน I / O และตัวจัดรูปแบบข้อความที่ตีความเช่นนั้น
verdy_p

ดังนั้นในสตริง C # คุณสามารถจัดเก็บรหัสหน่วยเช่น 0xFFFF หรือ 0xFFFE ได้อย่างปลอดภัยแม้ว่าจะไม่ใช่อักขระใน UTF-16 และคุณสามารถเก็บ 0xD800 แยกได้ตามด้วยหน่วยรหัสใน 0xDC00..0xDFFF (เช่น ตัวแทนสำรองที่ไม่ได้จับคู่ซึ่งไม่ถูกต้องใน UTF-16) หมายเหตุเดียวกันนี้ใช้กับสตริงใน Javascript / ECMAscript และ Java
verdy_p

เมื่อคุณใช้ "GetBytes" แน่นอนคุณไม่ได้ระบุการเข้ารหัส แต่คุณถือว่าคำสั่ง byte เพื่อรับสองไบต์ใน specic สำหรับหน่วยโค้ดแต่ละหน่วยที่เก็บไว้ในสตริง เมื่อคุณสร้างสตริงใหม่จากไบต์คุณต้องมีตัวแปลงไม่จำเป็นต้องเป็น UTF-8 ถึง UTF-16 คุณสามารถแทรก 0 พิเศษในไบต์สูงหรือแพ็คสองไบต์ (ใน MSB ก่อนหรือ LSB ลำดับแรก) ใน หน่วยรหัส 16 บิตเดียวกัน สตริงนั้นเป็นรูปแบบกะทัดรัดสำหรับอาร์เรย์จำนวนเต็ม 16 บิต ความสัมพันธ์กับ "ตัวอักษร" เป็นอีกปัญหาหนึ่งใน C # ไม่ใช่ประเภทจริงเนื่องจากยังแสดงเป็นสตริง
verdy_p

7

วิธีการที่ใกล้เคียงที่สุดกับคำถามของ OP คือ Tom Blodget ซึ่งเข้าสู่วัตถุและแยกไบต์ ฉันพูดใกล้เคียงที่สุดเพราะมันขึ้นอยู่กับการดำเนินการของวัตถุ String

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

แน่นอน แต่นั่นคือสิ่งที่เกิดข้อผิดพลาดพื้นฐานในคำถาม String เป็นวัตถุซึ่งอาจมีโครงสร้างข้อมูลที่น่าสนใจ เรารู้แล้วว่ามันเป็นเช่นนั้นเพราะมันช่วยให้ตัวแทนอุ้มท้องที่ไม่มีคู่สมรสถูกเก็บไว้ มันอาจเก็บความยาว มันอาจเก็บตัวชี้ไปยังตัวแทนอุ้มท้อง 'จับคู่' แต่ละตัวเพื่อให้นับได้อย่างรวดเร็ว เป็นต้นไบต์พิเศษทั้งหมดเหล่านี้ไม่ได้เป็นส่วนหนึ่งของข้อมูลตัวอักษร

สิ่งที่คุณต้องการคือไบต์ของตัวละครแต่ละตัวในอาเรย์ และนั่นคือที่ 'เข้ารหัส' เข้ามาโดยค่าเริ่มต้นคุณจะได้รับ UTF-16LE หากคุณไม่สนใจไบต์ตัวเองยกเว้นการไปกลับคุณสามารถเลือกการเข้ารหัสใด ๆ รวมถึง 'ค่าเริ่มต้น' และแปลงกลับในภายหลัง (สมมติว่าพารามิเตอร์เดียวกันเช่นการเข้ารหัสเริ่มต้นคือรหัสจุดการแก้ไขข้อบกพร่อง สิ่งที่ได้รับอนุญาตเช่นตัวแทนอุ้มท้อง ฯลฯ

แต่ทำไมปล่อยให้ 'เข้ารหัส' ถึงความมหัศจรรย์? ทำไมไม่ระบุการเข้ารหัสเพื่อให้คุณรู้ว่าคุณจะได้ไบต์อะไร

"Why is there a dependency on character encodings?"

การเข้ารหัส (ในบริบทนี้) หมายถึงไบต์ที่แทนสตริงของคุณ ไม่ใช่ไบต์ของวัตถุสตริง คุณต้องการไบต์ที่เก็บสตริงไว้ - นี่เป็นที่ที่คำถามถูกถามอย่างไร้เดียงสา คุณต้องการไบต์ของสตริงในอาร์เรย์ที่ต่อเนื่องกันซึ่งแสดงถึงสตริงและไม่ใช่ข้อมูลไบนารีอื่น ๆ ทั้งหมดที่อาจมีวัตถุสตริง

ซึ่งหมายความว่าการจัดเก็บสตริงนั้นไม่เกี่ยวข้อง คุณต้องการสตริง "เข้ารหัส" เป็นไบต์ในอาร์เรย์ไบต์

ฉันชอบคำตอบของ Tom Bloget เพราะเขาพาคุณไปสู่ทิศทาง 'ไบต์ของวัตถุสตริง' มันขึ้นอยู่กับการนำไปใช้และเพราะเขาดูที่ internals มันอาจจะเป็นการยากที่จะสร้างสำเนาของสตริงขึ้นใหม่

คำตอบของ Mehrdad นั้นผิดเพราะมันทำให้เข้าใจผิดในระดับแนวคิด คุณยังมีรายการของไบต์เข้ารหัสอยู่ วิธีแก้ปัญหาเฉพาะของเขาอนุญาตให้เก็บรักษาตัวแทนเสมือนที่ไม่มีการจับคู่ไว้ โซลูชันเฉพาะของเขาจะไม่สร้างไบต์ของสตริงอย่างถูกต้องหากGetBytesส่งคืนสตริงใน UTF-8 ตามค่าเริ่มต้น


ฉันเปลี่ยนใจเกี่ยวกับสิ่งนี้ (โซลูชันของ Mehrdad) - นี่ไม่ได้รับจำนวนไบต์ของสตริง ค่อนข้างจะได้รับไบต์ของอาร์เรย์ตัวละครที่ถูกสร้างขึ้นจากสตริง ไม่ว่าจะมีการเข้ารหัสรูปแบบถ่านประเภทใดใน c # เป็นขนาดคงที่ สิ่งนี้ช่วยให้สามารถสร้างอาร์เรย์ไบต์ความยาวที่สอดคล้องกันและอนุญาตให้อาร์เรย์อักขระซ้ำตามขนาดของอาร์เรย์ไบต์ ดังนั้นหากการเข้ารหัสเป็น UTF-8 แต่ถ่านแต่ละตัวมีขนาด 6 ไบต์เพื่อรองรับค่า utf8 ที่ใหญ่ที่สุดมันจะยังคงทำงานได้ ดังนั้นการเข้ารหัสตัวละครจึงไม่สำคัญ

แต่มีการใช้การแปลง - อักขระแต่ละตัวถูกวางไว้ในกล่องขนาดคงที่ (ประเภทตัวอักษรของ c #) อย่างไรก็ตามสิ่งที่นำเสนอนั้นไม่สำคัญซึ่งเป็นคำตอบทางเทคนิคต่อ OP ดังนั้น - ถ้าคุณจะแปลงต่อไป ... ทำไมไม่ 'เข้ารหัส'?


ตัวละครเหล่านี้จะได้รับการสนับสนุนโดย UTF-8 หรือ UTF-16 หรือแม้กระทั่ง UTF-32 สำหรับ exapmle: 񩱠& &(Char) 55906 (Char) 55655ดังนั้นคุณอาจผิดและคำตอบของ Mehrdad เป็นการแปลงที่ปลอดภัยโดยไม่คำนึงถึงชนิดของการเข้ารหัสที่ใช้
Mojtaba Rezaeian

Raymon อักขระจะถูกแสดงด้วยค่ายูนิโค้ดบางส่วนแล้วและค่า Unicode ทั้งหมดสามารถแสดงโดย utf ทั้งหมดได้ มีคำอธิบายที่ยาวขึ้นเกี่ยวกับสิ่งที่คุณกำลังพูดถึงหรือไม่? มีการเข้ารหัสอักขระใดที่ค่าสองค่า (หรือ 3 .. ) มีอยู่ในนั้น?
เจอราร์ดโอนีล

เป็นอักขระที่ไม่ถูกต้องซึ่งไม่รองรับช่วงการเข้ารหัสใด ๆ นี่ไม่ได้หมายความว่ามันไร้ประโยชน์ 100% รหัสที่แปลงสตริงชนิดใด ๆ ให้เทียบเท่าอาร์เรย์ไบต์โดยไม่คำนึงถึงการเข้ารหัสไม่ใช่วิธีการที่ผิดเลยและมีการใช้งานของตัวเองในโอกาสที่ต้องการ
Mojtaba Rezaeian

1
ตกลงฉันคิดว่าคุณไม่เข้าใจปัญหา เรารู้ว่ามันเป็นอาร์เรย์ที่เข้ากันได้กับ Unicode - อันที่จริงเพราะเป็น. net เรารู้ว่าเป็น UTF-16 ดังนั้นตัวละครเหล่านั้นจะไม่มีอยู่จริง คุณยังไม่ได้อ่านความคิดเห็นของฉันเกี่ยวกับการเปลี่ยนแปลงการนำเสนอภายในอย่างเต็มที่ String เป็นวัตถุไม่ใช่อาร์เรย์ไบต์ที่เข้ารหัส ดังนั้นฉันจะไม่เห็นด้วยกับคำสั่งสุดท้ายของคุณ คุณต้องการให้โค้ดแปลงสตริงยูนิโค้ดทั้งหมดเป็นการเข้ารหัส UTF ใด ๆ นี่คือสิ่งที่คุณต้องการอย่างถูกต้อง
เจอราร์ดโอนีล

วัตถุคือลำดับของข้อมูลลำดับของบิตที่อธิบายถึงวัตถุในสถานะปัจจุบัน ดังนั้นข้อมูลทั้งหมดในภาษาการเขียนโปรแกรมสามารถแปลงเป็นอาร์เรย์ของไบต์ (แต่ละไบต์กำหนด 8 บิต) เนื่องจากคุณอาจต้องเก็บสถานะของวัตถุใด ๆ ไว้ในหน่วยความจำ คุณสามารถบันทึกลำดับของไบต์ในไฟล์หรือหน่วยความจำค้างไว้และแปลงเป็นจำนวนเต็มบิ๊กอินรูปภาพสตริง ASCII สตริง UTF-8 สตริงเข้ารหัสหรือประเภทข้อมูลที่คุณกำหนดเองหลังจากอ่านจากดิสก์ ดังนั้นคุณไม่สามารถพูดได้ว่าวัตถุเป็นสิ่งที่แตกต่างจากลำดับไบต์
Mojtaba Rezaeian

6

คุณสามารถใช้รหัสต่อไปนี้เพื่อแปลง a 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;
}

มันเร็วกว่าวิธีที่ผู้ตอบรับที่ยอมรับแม้ว่าจะไม่ได้สง่างามเท่าที่ควร นี่คือมาตรฐานของนาฬิกาจับเวลาของฉันซ้ำกว่า 10,000,000 ครั้ง:

[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 วิธีนี้สามารถใช้เป็นส่วนขยายของสตริงได้ด้วย:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}

มูลค่าของRuntimeHelpers.OffsetToStringDataตัวคูณของ 8 บน. NET ของ Itanium รุ่นหรือไม่ เพราะมิฉะนั้นสิ่งนี้จะล้มเหลวเนื่องจากการอ่านที่ไม่ได้จัดแนว
Jon Hanna

มันจะง่ายกว่า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 code ในสตริงจริงเช่น ASCII) ภายใน. NET รองรับ UTF-16 แต่สำหรับการแสดงสตรีมมักใช้ UTF-8 นอกจากนี้ยังเป็นมาตรฐานสำหรับอินเทอร์เน็ต

ไม่น่าแปลกใจที่การเรียงลำดับของสตริงลงในอาร์เรย์ของไบต์และดีซีเรียลไลเซชันรองรับโดยคลาสSystem.Text.Encodingซึ่งเป็นคลาสนามธรรม คลาสที่ได้รับสนับสนุนการเข้ารหัสที่เป็นรูปธรรม: ASCIIEncodingและSystem.Text.UnicodeEncodingUTF สี่ตัว ( รองรับ UTF-16)

อ้างอิงลิงค์นี้

สำหรับซีเรียลSystem.Text.Encoding.GetBytesไลซ์เซชั่นเป็นอาร์เรย์ของไบต์ที่ใช้ System.Text.Encoding.GetCharsสำหรับการใช้งานการดำเนินการผกผัน ฟังก์ชั่นนี้จะส่งกลับอาร์เรย์ของตัวอักษรเพื่อที่จะได้รับสตริงใช้ System.String(char[])constructor
อ้างอิงหน้านี้

ตัวอย่าง:

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

ขึ้นอยู่กับสิ่งที่คุณต้องการไบต์

นี่เป็นเพราะอย่างที่ไทเลอร์พูดอย่างเหมาะสม"สตริงไม่ได้เป็นข้อมูลที่บริสุทธิ์ แต่ก็มีข้อมูลด้วย" ในกรณีนี้ข้อมูลเป็นการเข้ารหัสที่สันนิษฐานเมื่อสร้างสายอักขระ

สมมติว่าคุณมีข้อมูลไบนารี (แทนที่จะเป็นข้อความ) ที่เก็บไว้ในสตริง

นี่เป็นความคิดเห็นของ OP ตามคำถามของเขาเองและเป็นคำถามที่ถูกต้องหากฉันเข้าใจคำแนะนำของ OP ในกรณีใช้งาน

การจัดเก็บข้อมูลไบนารีในสตริงอาจเป็นวิธีที่ผิดเพราะการเข้ารหัสที่กล่าวมาข้างต้น! โปรแกรมหรือคลังใดก็ตามที่จัดเก็บข้อมูลไบนารีในstring(แทนที่จะเป็นbyte[]อาร์เรย์ที่น่าจะเหมาะสมกว่า) จะแพ้การต่อสู้ก่อนที่มันจะเริ่มขึ้น หากพวกเขากำลังส่งไบต์ให้คุณในคำขอ REST / การตอบสนองหรือสิ่งที่ต้องส่งสตริงBase64จะเป็นวิธีการที่เหมาะสม

หากคุณมีสตริงข้อความที่มีการเข้ารหัสที่ไม่รู้จัก

ทุกคนอื่นตอบคำถามไม่ถูกต้องนี้อย่างไม่ถูกต้อง

หากสตริงดูดีเหมือนเดิมเพียงแค่เลือกการเข้ารหัส (ควรเริ่มจาก UTF) โดยใช้System.Text.Encoding.???.GetBytes()ฟังก์ชั่นที่เกี่ยวข้องแล้วบอกผู้ที่คุณให้ไบต์ว่าคุณเลือกการเข้ารหัสแบบใด


2

เมื่อถูกถามว่าคุณตั้งใจจะทำอะไรกับไบต์คุณตอบ :

ฉันจะเข้ารหัสมัน ฉันสามารถเข้ารหัสได้โดยไม่แปลง แต่ฉันก็ยังอยากรู้ว่าทำไมการเข้ารหัสจึงมาเล่นที่นี่ เพียงแค่ให้ฉันไบต์คือสิ่งที่ฉันพูด

ไม่ว่าคุณตั้งใจจะส่งข้อมูลที่เข้ารหัสนี้ผ่านเครือข่ายโหลดกลับเข้าไปในหน่วยความจำในภายหลังหรือส่งไปยังกระบวนการอื่นคุณตั้งใจจะถอดรหัสอย่างชัดเจนในบางจุด ในกรณีนี้คำตอบคือคุณกำลังกำหนดโปรโตคอลการสื่อสาร ไม่ควรกำหนดโปรโตคอลการสื่อสารในแง่ของรายละเอียดการนำไปปฏิบัติของภาษาโปรแกรมของคุณ มีเหตุผลหลายประการนี้:

  • คุณอาจต้องสื่อสารกับกระบวนการที่ใช้ในภาษาอื่นหรือรันไทม์ (ซึ่งอาจรวมถึงเซิร์ฟเวอร์ที่ทำงานบนเครื่องอื่นหรือส่งสตริงไปยังเบราว์เซอร์ไคลเอนต์ JavaScript เป็นต้น)
  • โปรแกรมอาจถูกนำมาใช้อีกครั้งในภาษาอื่นหรือรันไทม์ในอนาคต
  • การใช้งาน. NET อาจเปลี่ยนการแสดงสตริงภายใน คุณอาจคิดว่ามันฟังดูไกลเกินไป แต่สิ่งนี้เกิดขึ้นจริงใน Java 9เพื่อลดการใช้หน่วยความจำ ไม่มีเหตุผล. NET ไม่สามารถทำตามความเหมาะสม Skeet แนะนำว่า 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
แล้วอักขระหลายไบต์ล่ะ
Agnel Kurian

c.ToByte () เป็นส่วนตัว: S
Khodor

@AgnelKurian Msdn พูดว่า "วิธีนี้จะส่งกลับค่าไบต์ที่ไม่ได้ลงนามซึ่งแสดงถึงรหัสตัวเลขของวัตถุ Char ที่ส่งไปยังมันใน. NET Framework วัตถุ Char เป็นค่า 16 บิตซึ่งหมายความว่าวิธีนี้เหมาะสำหรับการส่งคืน รหัสตัวเลขของอักขระในช่วงอักขระ ASCII หรือใน Unicode C0 Controls และ Basic Latin และช่วง C1 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.