Wie bekomme ich eine konsistente Byte-Darstellung von Strings in C # ohne manuelles Angeben einer Codierung? [c#]


Answers

Es hängt von der Codierung deiner Zeichenfolge ab ( ASCII , UTF-8 , ...).

Beispielsweise:

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

Ein kleines Beispiel, warum Kodierung:

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 ist einfach nicht für Sonderzeichen geeignet.

Intern verwendet das .NET Framework UTF-16 , um Strings darzustellen. Wenn Sie also einfach die genauen Bytes erhalten möchten, die .NET verwendet, verwenden Sie System.Text.Encoding.Unicode.GetBytes (...) .

Weitere Informationen finden Sie unter Zeichencodierung im .NET Framework (MSDN).

Question

Wie konvertiere ich einen string in ein byte[] in .NET (C #), ohne manuell eine spezifische Codierung anzugeben?

Ich werde die Zeichenfolge verschlüsseln. Ich kann es verschlüsseln, ohne zu konvertieren, aber ich würde immer noch gerne wissen, warum die Kodierung hier zu spielen kommt. Gib mir nur die Bytes, was ich sage.

Auch warum sollte die Kodierung berücksichtigt werden? Kann ich nicht einfach bekommen, welche Bytes der String gespeichert wurde? Warum gibt es eine Abhängigkeit von Zeichencodierungen?




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



Das ist eine beliebte Frage. Es ist wichtig zu verstehen, was die Frage Autor fragt, und dass es anders ist als das, was wahrscheinlich die häufigste Notwendigkeit ist. Um den Missbrauch des Codes zu entmutigen, wo es nicht nötig ist, habe ich die spätere Antwort beantwortet.

Gemeinsame Notwendigkeit

Jeder String hat einen Zeichensatz und eine Codierung. Wenn Sie ein System.String Objekt in ein Array von System.Byte Sie noch einen Zeichensatz und eine Codierung. Für die meisten Verwendungen, würden Sie wissen, welche Zeichensatz und Kodierung Sie brauchen und .NET macht es einfach zu "kopieren mit Umwandlung". Wählen Sie einfach die passende Encoding Klasse.

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

Die Konvertierung muss möglicherweise Fälle behandeln, in denen der Zielzeichensatz oder die Codierung kein Zeichen unterstützt, das sich in der Quelle befindet. Sie haben einige Möglichkeiten: Ausnahme, Substitution oder Überspringen. Die Standardrichtlinie besteht darin, ein '?' Zu ersetzen.

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

Klar, Conversions sind nicht unbedingt verlustfrei!

Hinweis: Für System.String der System.String Unicode.

Die einzige verwirrende Sache ist, dass .NET den Namen eines Zeichensatzes für den Namen einer bestimmten Codierung dieses Zeichensatzes verwendet. Encoding.Unicode sollte Encoding.UTF16 heißen.

Das ist es für die meisten verbrauch. Wenn du das brauchst, hörst du hier auf zu lesen Sehen Sie den Spaß Joel Spolsky Artikel, wenn Sie nicht verstehen, was eine Kodierung ist.

Spezifische Notwendigkeit

Nun fragt der Frage-Autor: "Jeder String wird als Array von Bytes gespeichert, richtig, warum kann ich nicht einfach diese Bytes haben?"

Er will keine Umwandlung.

Von der C # spec :

Zeichen- und Zeichenfolgenverarbeitung in C # verwendet Unicode-Codierung. Der char-Typ repräsentiert eine UTF-16-Codeeinheit, und der String-Typ repräsentiert eine Folge von UTF-16-Codeeinheiten.

Also, wir wissen, dass, wenn wir nach der Nullumwandlung (dh von UTF-16 nach UTF-16) fragen, wir das gewünschte Ergebnis bekommen:

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

Um die Erwähnung der Kodierungen zu vermeiden, müssen wir es anders machen. Wenn ein Zwischen-Datentyp akzeptabel ist, gibt es hier eine konzeptionelle Verknüpfung:

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

Das bekommt uns nicht den gewünschten Datentyp, aber Mehrdads Antwort zeigt, wie man dieses Char-Array mit BlockCopy in ein Byte-Array konvertiert . Allerdings kopiert das die Saite zweimal! Und es verwendet auch explizit kodierungsspezifischen Code: den Datentyp System.Char .

Der einzige Weg, um zu den tatsächlichen Bytes zu gelangen, in denen der String gespeichert ist, ist, einen Zeiger zu verwenden. Die fixed Anweisung erlaubt es, die Adresse der Werte zu nehmen. Von der C # spec:

[Für] ein Ausdruck von type string, ... der initializer berechnet die Adresse des ersten Zeichens in der Zeichenfolge.

Um dies zu tun, schreibt der Compiler Code überspringen über die anderen Teile des String-Objekts mit RuntimeHelpers.OffsetToStringData . Also, um die rohen Bytes zu bekommen, erstellen Sie einfach einen Zeiger auf die Zeichenfolge und kopieren Sie die Anzahl der benötigten Bytes.

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

Wie @CodesInChaos darauf hingewiesen hat, hängt das Ergebnis von der Endlichkeit der Maschine ab. Aber die Frage Autor ist nicht damit beschäftigt.




Versuchen Sie das, viel weniger Code:

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



Nun, ich habe alle Antworten gelesen und sie waren über die Verwendung von Codierung oder eine über Serialisierung, die unpaired Surrogate fällt.

Es ist schlecht, wenn die Saite zum Beispiel aus SQL Server kommt, wo sie aus einem Byte-Array gebaut wurde, z. B. ein Passwort-Hash. Wenn wir etwas davon ablegen, wird es einen ungültigen Hash speichern, und wenn wir es in XML speichern wollen, wollen wir es intakt lassen (weil der XML-Schriftsteller eine Ausnahme auf ein ungepanzertes Surrogat fällt, das es findet).

Also ich benutze Base64- Codierung von Byte-Arrays in solchen Fällen, aber hey, im Internet gibt es nur eine Lösung für diese in C #, und es hat Bug in es und ist nur ein Weg, also habe ich den Bug fixiert und geschrieben Verfahren. Hier sind Sie, zukünftige Googler:

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



C # um einen string in ein byte Array zu konvertieren:

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



Sie können den folgenden Code für die Konvertierung zwischen String und Byte-Array verwenden.

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



Die zentrale Frage ist, dass eine Glyphe in einem String 32 Bits nimmt (16 Bits für einen Zeichencode), sondern ein Byte hat nur 8 Bits verschonen. Eine Eins-zu-Eins-Abbildung existiert nicht, wenn Sie sich auf Strings beschränken, die nur ASCII-Zeichen enthalten. System.Text.Encoding hat viele Möglichkeiten, um eine Zeichenfolge zur Karte byte [], müssen Sie eine auswählen, die den Verlust von Informationen vermeidet und das ist einfach von Ihrem Client zu verwenden, wenn sie die byte [] zurück in eine Zeichenfolge zur Karte benötigt .

UTF-8 ist ein beliebtes Codierung ist es kompakt und nicht verlustbehaftet.




Benutzen:

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

Das Ergebnis ist:

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



Die größte Annäherung an die Frage des OP ist Tom Blodget ist, die das Objekt tatsächlich geht in und extrahiert die Bytes. Ich sage am nächsten, weil es bei der Umsetzung des String-Objekts abhängt.

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

Sicher, aber das ist, wo der grundlegende Fehler in der Frage stellt. Der String ist ein Objekt, das eine interessante Datenstruktur haben könnte. Wir wissen bereits, es funktioniert, weil es ungepaarten Surrogate gespeichert werden können. Es könnte die Länge speichern. Es könnte einen Zeiger auf jede der ‚gepaart‘ Surrogate ermöglicht eine schnelle Zählung halten. Usw. Alle diese zusätzlichen Bytes sind nicht Teil der Zeichendaten.

Was Sie wollen, ist jeder Byte Charakter in einem Array. Und das ist, wo ‚encoding‘ kommt in. In der Standardeinstellung werden Sie UTF-16LE bekommen. Wenn Sie nicht über die Bytes kümmern sich außer für die Hin- und Rückfahrt dann können Sie jede Codierung, einschließlich der ‚default‘, wählen und es später zurückwandeln (die gleichen Parameter unter der Annahme, wie, was die Standard-Kodierung war, Codepunkte, Fehlerbehebungen Dinge erlaubt wie ungepaarten Surrogate, usw.

Aber warum lassen Sie die ‚encoding‘ bis zu Magie? Warum geben Sie die Codierung nicht, so dass Sie wissen, was Bytes Sie kriegen?

"Why is there a dependency on character encodings?"

Encoding (in diesem Zusammenhang) bedeutet einfach das Bytes, die die Zeichenfolge darstellen. Nicht der Bytes des String-Objekts. Sie wollten die Bytes der Zeichenfolge in gespeichert wurde - das ist, wo die Frage naiv gefragt wurde. Sie wollten die Bytes der Zeichenkette in einem zusammenhängenden Array, das die Zeichenfolge darstellen, und nicht alle anderen binären Daten, die ein String-Objekt enthalten kann.

Was bedeutet, wie ein String gespeichert ist, ist irrelevant. Sie wollen einen String „Verschlüsselte“ in Bytes in einem Byte-Array.

Ich mag Tom Bloget Antwort, weil er sich auf das ‚Bytes der String-Objekt‘ nahm Richtung. Es ist abhängig von der Implementierung aber, und weil er Interna spähen ist könnte es schwierig sein, eine Kopie der Zeichenfolge zu rekonstruieren.

Mehrdad Antwort ist falsch , weil es auf der konzeptionellen Ebene irreführend. Sie haben noch eine Liste von Bytes, codiert. Seine besondere Lösung ermöglicht ungepaarten Surrogate bewahrt werden - dies ist abhängig von der Implementierung. Seine besondere Lösung würde die Zeichenfolge des Bytes genau nicht erzeugen , wenn GetBytesdie Zeichenfolge in UTF-8 standardmäßig zurückgegeben.




Zwei Wege:

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

Und,

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

Ich neige dazu, den Boden ein häufiger als die Spitze zu verwenden, haben sie nicht für Geschwindigkeit gebenchmarkt.




einfacher Code mit LINQ

string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();

EDIT: wie weiter unten kommentiert, es ist kein guter Weg.

aber man kann es immer noch verwenden LINQ zu verstehen, mit einer geeigneteren Codierung:

string s = "abc"
byte[] b = s.Cast<byte>().ToArray();



Wenn Sie wirklich eine Kopie der zugrunde liegenden Bytes eines Strings möchten, können Sie eine Funktion wie die eine , die folgt. Allerdings sollten Sie nicht lesen Sie bitte weiter , um herauszufinden , warum.

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

Mit dieser Funktion erhalten Sie eine Kopie der Bytes erhalten Sie Ihre Zeichenfolge zugrunde liegen, ziemlich schnell. Sie werden diese Bytes in welcher Weise auch immer sie kodieren, die auf dem System erhalten. Diese Codierung ist mit ziemlicher Sicherheit UTF-16LE, aber das ist eine Implementierung Detail sollte man nicht haben zu kümmern.

Es wäre sicherer, einfacher und zuverlässiger zu rufen Sie einfach an,

System.Text.Encoding.Unicode.GetBytes()

Aller Wahrscheinlichkeit nach wird dies das gleiche Ergebnis, ist einfacher zu tippen, und die Bytes werden immer Round-Trip mit einem Aufruf an

System.Text.Encoding.Unicode.GetString()



// C# to convert a string to a byte array.
public static byte[] StrToByteArray(string str)
{
    System.Text.ASCIIEncoding  encoding=new System.Text.ASCIIEncoding();
    return encoding.GetBytes(str);
}


// C# to convert a byte array to a string.
byte [] dBytes = ...
string str;
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
str = enc.GetString(dBytes);



Von byte[]bis string:

        return BitConverter.ToString(bytes);