Frage Wie bekomme ich eine konsistente Byte-Darstellung von Zeichenfolgen in C #, ohne manuell eine Codierung festzulegen?


Wie konvertiere ich ein string zu einem byte[] in .NET (C #), ohne manuell eine bestimmte Kodierung anzugeben?

Ich werde die Zeichenfolge verschlüsseln. Ich kann es ohne Konvertierung verschlüsseln, aber ich würde immer noch gerne wissen, warum die Codierung hier zum Einsatz kommt.

Auch warum sollte Codierung in Betracht gezogen werden? Kann ich nicht einfach herausfinden, in welchen Bytes die Zeichenfolge gespeichert wurde? Warum besteht eine Abhängigkeit von Zeichenkodierungen?


1909
2018-01-23 13:39


Ursprung


Antworten:


Im Gegensatz zu den Antworten müssen Sie sich nicht um die Codierung kümmern ob Die Bytes müssen nicht interpretiert werden!

Wie du schon erwähnt hast, ist dein Ziel einfach "Erhalte, in welche Bytes die Zeichenfolge gespeichert wurde".
(Und natürlich, um die Zeichenfolge aus den Bytes neu zu konstruieren.)

Für diese Ziele tue ich ehrlich gesagt nicht verstehe, warum die Leute dir immer wieder sagen, dass du die Kodierungen benötigst. Sie müssen sich sicherlich nicht um Kodierungen kümmern.

Tun Sie das stattdessen:

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

Solange Ihr Programm (oder andere Programme) nicht versuchen interpretieren die Bytes irgendwie, von denen du offensichtlich nicht erwähnt hast, dass du vorhast, dann gibt es sie nichts falsch mit diesem Ansatz! Sich über Codierungen Sorgen zu machen, macht dein Leben ohne wirklichen Grund komplizierter.

Zusätzlicher Vorteil für diesen Ansatz:

Es spielt keine Rolle, ob die Zeichenfolge ungültige Zeichen enthält, da Sie die Daten trotzdem erhalten und die ursprüngliche Zeichenfolge trotzdem rekonstruieren können!

Es wird genau so codiert und decodiert, wie Sie es sind nur auf die Bytes schauen.

Wenn Sie jedoch eine bestimmte Kodierung verwendet hätten, hätten Sie Probleme mit der Kodierung / Dekodierung ungültiger Zeichen gehabt.


1721
2018-04-30 07:44



Es hängt von der Codierung Ihrer 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 Codierung wichtig ist:

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 mit Sonderzeichen ausgestattet.

Intern verwendet das .NET-Framework UTF-16 Um Strings darzustellen, sollten Sie einfach die genauen Bytes verwenden, die .NET verwendet System.Text.Encoding.Unicode.GetBytes (...).

Sehen Zeichencodierung in .NET Framework (MSDN) für weitere Informationen.


1052
2018-01-23 13:43



Die angenommene Antwort ist sehr, sehr kompliziert. Verwenden Sie dazu die enthaltenen .NET-Klassen:

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

Erfinde das Rad nicht neu, wenn du nicht musst ...


245
2018-04-30 07:26



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

105
2018-01-23 16:36



Sie müssen die Codierung berücksichtigen, da 1 Zeichen durch 1 dargestellt werden könnte oder mehr Bytes (bis zu etwa 6) und unterschiedliche Codierungen behandeln diese Bytes unterschiedlich.

Joel hat einen Beitrag dazu:

Das absolute Minimum, das jeder Softwareentwickler unbedingt und unbedingt über Unicode und Zeichensätze wissen muss (keine Ausreden!)


79
2018-01-23 14:03



Dies ist eine beliebte Frage. Es ist wichtig zu verstehen, was der Fragesteller fragt und dass es sich von dem wahrscheinlichsten Bedürfnis unterscheidet. Um den Missbrauch des Codes, wo es nicht benötigt wird, zu entmutigen, habe ich das erstere beantwortet.

Gemeinsame Notwendigkeit

Jede Zeichenfolge hat einen Zeichensatz und eine Codierung. Wenn Sie ein System.String Objekt auf ein Array von System.Byte Du hast immer noch einen Zeichensatz und eine Kodierung. Für die meisten Anwendungen wissen Sie, welchen Zeichensatz und welche Kodierung Sie benötigen, und .NET vereinfacht das Kopieren mit der Konvertierung. Wähle einfach das Passende aus 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 Zeichencodierung kein Zeichen unterstützt, das sich in der Quelle befindet. Sie haben einige Auswahlmöglichkeiten: Ausnahme, Ersetzung 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"

Natürlich sind Conversions nicht unbedingt verlustfrei!

Hinweis: Für System.String Der Quellzeichensatz ist Unicode.

Die einzige verwirrende Sache ist, dass .NET den Namen eines Zeichensatzes für den Namen einer bestimmten Kodierung dieses Zeichensatzes verwendet. Encoding.Unicode sollte angerufen werden Encoding.UTF16.

Das ist es für die meisten Verwendungen. Wenn Sie das brauchen, hören Sie hier auf. Sieh den Spaß Joel Spolsky Artikel wenn Sie nicht verstehen, was eine Kodierung ist.

Spezifische Notwendigkeit

Jetzt fragt der Autor der Frage: "Jede Zeichenfolge wird als Array von Bytes gespeichert, richtig? Warum kann ich diese Bytes nicht einfach haben?"

Er möchte keine Konvertierung.

Von dem C # -Spezifikation:

Zeichen- und Zeichenfolgeverarbeitung in C # verwendet Unicode-Codierung. Das Zeichen   type stellt eine UTF-16-Codeeinheit dar und der Zeichenfolientyp repräsentiert a   Sequenz von UTF-16-Code-Einheiten.

Wir wissen also, dass wir das gewünschte Ergebnis erhalten, wenn wir nach der Nullumwandlung (also von UTF-16 nach UTF-16) fragen:

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

Aber um die Erwähnung von Codierungen zu vermeiden, müssen wir es anders machen. Wenn ein Zwischendatentyp akzeptabel ist, gibt es dafür eine konzeptionelle Verknüpfung:

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

Das bringt uns aber nicht den gewünschten Datentyp Mehrdads Antwort zeigt, wie man dieses Char-Array mit einem Byte-Array umwandelt Blockkopie. Dies kopiert jedoch die Zeichenfolge zweimal! Und es verwendet auch explizit codierungsspezifischen Code: den Datentyp System.Char.

Die einzige Möglichkeit, zu den tatsächlichen Bytes zu gelangen, in denen die Zeichenfolge gespeichert ist, ist die Verwendung eines Zeigers. Das fixed Anweisung erlaubt die Adresse von Werten zu nehmen. Aus der C # -Spezifikation:

[Für] einen Ausdruck vom Typ string, ... berechnet der Initialisierer den   Adresse des ersten Zeichens in der Zeichenfolge.

Um dies zu tun, schreibt der Compiler Code über die anderen Teile des String-Objekts mit RuntimeHelpers.OffsetToStringData. Um also die rohen Bytes zu erhalten, 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 gezeigt hat, hängt das Ergebnis von der Endianess der Maschine ab. Aber der Fragesteller kümmert sich nicht darum.


76
2017-12-02 04:43



Nur um den Sound von Mehrdrad zu demonstrieren Antworten funktioniert, kann seine Vorgehensweise sogar bestehen bleiben unpaare Ersatzzeichen(von denen viele gegen meine Antwort geächtet hatten, von denen aber alle gleichermaßen schuldig sind, z.B. System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; Diese Codierungsmethoden können die hohen Ersatzzeichen nicht beibehalten d800zum Beispiel, und diese ersetzen einfach nur hohe Ersatzzeichen durch Wert 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);
    }        
}

Ausgabe:

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

Probieren Sie das mit System.Text.Encoding.UTF8.GetBytes oder System.Text.Encoding.Unicode.GetBytes, sie werden nur Ersatzzeichen mit Wert ersetzen fffd

Jedes Mal, wenn eine Bewegung in dieser Frage auftritt, denke ich immer noch an einen Serialisierer (sei es von Microsoft oder von einer Drittanbieterkomponente), der Zeichenfolgen beibehalten kann, selbst wenn er nicht ersetzte Ersatzzeichen enthält. Ich google das immer wieder: Serialisierung nichtpaaren Ersatzzeichen .NET. Das lässt mich nicht schlafen, aber es ist irgendwie nervig, wenn ab und zu jemand meine Antwort kommentiert, dass sie fehlerhaft ist, aber ihre Antworten sind ebenso fehlerhaft, wenn es um ungepackte Ersatzcharaktere geht.

Darn, hätte Microsoft gerade gebraucht System.Buffer.BlockCopy in seinem BinaryFormatter ツ

谢谢!


35
2017-07-25 22:52



Versuchen Sie dies, viel weniger Code:

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

34
2018-01-23 15:54