Wie erhalte ich eine konsistente Byte-Darstellung von Zeichenfolgen in C #, ohne manuell eine Codierung anzugeben?

2189

Wie konvertiere ich eine stringin eine byte[]in .NET (C #), ohne manuell eine bestimmte Codierung anzugeben?

Ich werde die Zeichenfolge verschlüsseln. Ich kann es verschlüsseln, ohne es zu konvertieren, aber ich möchte trotzdem wissen, warum hier die Codierung zum Tragen kommt.

Warum sollte die Kodierung überhaupt berücksichtigt werden? Kann ich nicht einfach abrufen, in welchen Bytes die Zeichenfolge gespeichert wurde? Warum besteht eine Abhängigkeit von Zeichenkodierungen?

Agnel Kurian
quelle
23
Jede Zeichenfolge wird als Array von Bytes gespeichert, oder? Warum kann ich diese Bytes nicht einfach haben?
Agnel Kurian
135
Die Codierung ist , was bildet die Zeichen auf das Bytes. In ASCII entspricht der Buchstabe 'A' beispielsweise der Nummer 65. In einer anderen Codierung ist er möglicherweise nicht identisch. Der allgemeine Ansatz für Zeichenfolgen im .NET-Framework macht dies jedoch weitgehend irrelevant (außer in diesem Fall).
Lucas Jones
20
Um Devil's Advocate zu spielen: Wenn Sie die Bytes einer In-Memory-Zeichenfolge abrufen möchten (wie .NET sie verwendet) und sie irgendwie manipulieren möchten (z. B. CRC32), und NIEMALS sie wieder in die ursprüngliche Zeichenfolge dekodieren möchten ... es Es ist nicht einfach, warum Sie sich für Codierungen interessieren oder wie Sie die zu verwendende auswählen.
Greg
78
Überrascht hat noch niemand diesen Link gegeben: joelonsoftware.com/articles/Unicode.html
Bevan
28
Ein Zeichen ist kein Byte und ein Byte ist kein Zeichen. Ein Zeichen ist sowohl ein Schlüssel für eine Schriftartentabelle als auch eine lexikalische Tradition. Eine Zeichenfolge ist eine Folge von Zeichen. (Wörter, Absätze, Sätze und Titel haben auch ihre eigenen lexikalischen Traditionen, die ihre eigenen Typdefinitionen rechtfertigen - aber ich schweife ab). Wie Ganzzahlen, Gleitkommazahlen und alles andere werden Zeichen in Bytes codiert. Es gab eine Zeit, in der die Codierung eins zu eins einfach war: ASCII. Um die gesamte menschliche Symbologie zu berücksichtigen, waren die 256 Permutationen eines Bytes jedoch unzureichend, und Codierungen wurden entwickelt, um selektiv mehr Bytes zu verwenden.
George

Antworten:

1855

Im Gegensatz zu den Antworten hier müssen Sie sich keine Gedanken über die Codierung machen, wenn die Bytes nicht interpretiert werden müssen!

Wie Sie bereits erwähnt haben, besteht Ihr Ziel einfach darin, "herauszufinden, in welchen Bytes die Zeichenfolge gespeichert wurde" .
(Und natürlich, um den String aus den Bytes rekonstruieren zu können.)

Für diese Ziele verstehe ich ehrlich gesagt nicht , warum die Leute Ihnen immer wieder sagen, dass Sie die Kodierungen benötigen. Sie müssen sich hierfür KEINE Gedanken über Codierungen machen.

Tun Sie dies stattdessen einfach:

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

Solange Ihr Programm (oder andere Programme) nicht versucht, die Bytes irgendwie zu interpretieren , was Sie offensichtlich nicht erwähnt haben, ist an diesem Ansatz nichts auszusetzen! Wenn Sie sich um Kodierungen sorgen, wird Ihr Leben ohne wirklichen Grund komplizierter.

Zusätzlicher Vorteil dieses Ansatzes:

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

Es wird trotzdem codiert und decodiert, da Sie nur die Bytes betrachten .

Wenn Sie jedoch eine bestimmte Codierung verwendet hätten, hätten Sie Probleme beim Codieren / Decodieren ungültiger Zeichen.

user541686
quelle
247
Was an diesem hässlich ist, ist, dass GetStringund GetBytesauf einem System mit der gleichen Endianness ausgeführt werden müssen, um zu arbeiten. Sie können dies also nicht verwenden, um Bytes abzurufen, die Sie an anderer Stelle in eine Zeichenfolge umwandeln möchten. Es fällt mir also schwer, Situationen zu finden, in denen ich dies nutzen möchte.
CodesInChaos
72
@CodeInChaos: Wie ich bereits sagte, ist der springende Punkt, wenn Sie es auf demselben System mit denselben Funktionen verwenden möchten. Wenn nicht, sollten Sie es nicht verwenden.
user541686
193
-1 Ich garantiere, dass jemand (der Bytes gegen Zeichen nicht versteht) seine Zeichenfolge in ein Byte-Array konvertieren möchte, sie wird es googeln und diese Antwort lesen, und sie werden das Falsche tun, weil in fast allen Fälle, die Codierung IS relevant.
Artbristol
401
@artbristol: Wenn sie sich nicht die Mühe machen können, die Antwort (oder die anderen Antworten ...) zu lesen, dann tut es mir leid, dann gibt es keinen besseren Weg für mich, mit ihnen zu kommunizieren. Ich entscheide mich im Allgemeinen für die Beantwortung des OP, anstatt zu erraten, was andere mit meiner Antwort machen könnten - das OP hat das Recht zu wissen, und nur weil jemand ein Messer missbrauchen könnte, heißt das nicht, dass wir alle Messer der Welt verstecken müssen für uns. Wenn Sie nicht einverstanden sind, ist das auch in Ordnung.
user541686
185
Diese Antwort ist auf so vielen Ebenen falsch, vor allem aber wegen der Dekleration "Sie müssen sich keine Sorgen um die Codierung machen!". Die beiden Methoden GetBytes und GetString sind insofern überflüssig, als sie lediglich Neuimplementierungen dessen sind, was Encoding.Unicode.GetBytes () und Encoding.Unicode.GetString () bereits tun. Die Aussage "Solange Ihr Programm (oder andere Programme) nicht versuchen, die Bytes zu interpretieren" ist ebenfalls grundlegend fehlerhaft, da sie implizit bedeutet, dass die Bytes als Unicode interpretiert werden sollten.
David
1108

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

Zum Beispiel:

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 für Sonderzeichen gerüstet.

Intern verwendet das .NET-Framework UTF-16 zur Darstellung von Zeichenfolgen. Wenn Sie also einfach die genauen Bytes abrufen möchten, die .NET verwendet, verwenden Sie System.Text.Encoding.Unicode.GetBytes (...).

Weitere Informationen finden Sie unter Zeichenkodierung in .NET Framework (MSDN).

bmotmans
quelle
14
Aber warum sollte die Codierung berücksichtigt werden? Warum kann ich die Bytes nicht einfach abrufen, ohne sehen zu müssen, welche Codierung verwendet wird? Sollte das String-Objekt selbst dann nicht wissen, welche Codierung verwendet wird, wenn es erforderlich wäre, und einfach den Speicherauszug sichern?
Agnel Kurian
57
Eine .NET-Zeichenfolge wird immer als Unicode codiert. Verwenden Sie also System.Text.Encoding.Unicode.GetBytes (); um den Satz von Bytes abzurufen, den .NET zur Darstellung der Zeichen verwenden würde. Aber warum willst du das? Ich empfehle UTF-8 besonders, wenn sich die meisten Zeichen im westlichen Latein befinden.
AnthonyWJones
8
Außerdem: Die genauen Bytes, die intern in der Zeichenfolge verwendet werden, spielen keine Rolle, wenn das System, das sie abruft, diese Codierung nicht oder als falsche Codierung behandelt. Wenn alles in .Net enthalten ist, warum überhaupt in ein Array von Bytes konvertieren? Ansonsten ist es besser, explizit mit Ihrer Kodierung
umzugehen
11
@Joel, seien Sie vorsichtig mit System.Text.Encoding.Default, da es auf jedem Computer, auf dem es ausgeführt wird, unterschiedlich sein kann. Aus diesem Grund wird empfohlen, immer eine Codierung wie UTF-8 anzugeben.
Ash
25
Sie benötigen die Codierungen nur, wenn Sie (oder eine andere Person) tatsächlich beabsichtigen, die Daten zu interpretieren , anstatt sie als generischen "Byteblock" zu behandeln. Für Dinge wie Komprimierung, Verschlüsselung usw. ist es bedeutungslos, sich Gedanken über die Codierung zu machen. In meiner Antwort finden Sie eine Möglichkeit, dies zu tun, ohne sich um die Codierung kümmern zu müssen. (Ich könnte eine -1 gegeben haben, um zu sagen, dass Sie sich um Codierungen sorgen müssen, wenn Sie dies nicht tun, aber ich fühle mich heute nicht besonders gemein .: P)
user541686
285

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

Erfinden Sie das Rad nicht neu, wenn Sie nicht ...

Erik A. Brandstadmoen
quelle
14
Falls die akzeptierte Antwort zu Aufzeichnungszwecken geändert wird, handelt es sich um Mehrdads Antwort zu diesem aktuellen Zeitpunkt und Datum. Hoffentlich wird das OP dies erneut prüfen und eine bessere Lösung akzeptieren.
Thomas Eding
7
Im Prinzip gut, aber die Kodierung sollte System.Text.Encoding.Unicodeder Antwort von Mehrdad entsprechen.
Jodrell
5
Die Frage wurde seit der ursprünglichen Antwort millionenfach bearbeitet. Vielleicht ist meine Antwort etwas veraltet. Ich hatte nie vor, ein Exace zu geben, das Mehrdads Antwort entspricht, sondern einen vernünftigen Weg zu finden. Aber Sie könnten Recht haben. Der Ausdruck "Abrufen, in welchen Bytes die Zeichenfolge gespeichert wurde" in der ursprünglichen Frage ist jedoch sehr ungenau. Wo gelagert? In Erinnerung? Auf der Festplatte? Wenn in Erinnerung, System.Text.Encoding.Unicode.GetByteswäre wahrscheinlich genauer.
Erik A. Brandstadmoen
7
@AMissico, Ihr Vorschlag ist fehlerhaft, es sei denn, Sie sind sicher, dass Ihre Zeichenfolge mit Ihrer Systemstandardcodierung kompatibel ist (Zeichenfolge, die nur ASCII-Zeichen in Ihrem Standard-Legacy-Zeichensatz Ihres Systems enthält). Aber nirgends sagt das OP das aus.
Frédéric
5
@AMissico Es kann jedoch dazu führen, dass das Programm auf verschiedenen Systemen unterschiedliche Ergebnisse liefert . Das ist nie gut. Selbst wenn es darum geht, einen Hash oder etwas anderes zu erstellen (ich nehme an, das bedeutet OP mit "verschlüsseln"), sollte dieselbe Zeichenfolge immer denselben Hash geben.
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
quelle
2
Sie könnten für alle diese Operationen dieselbe BinaryFormatter-Instanz verwenden
Joel Coehoorn,
3
Sehr interessant. Anscheinend wird jedes hohe Ersatz-Unicode-Zeichen gelöscht. Siehe die Dokumentation zu [BinaryFormatter ]
95

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

Joel hat einen Beitrag dazu:

Das absolute Minimum Jeder Softwareentwickler muss unbedingt positiv über Unicode und Zeichensätze Bescheid wissen (keine Ausreden!)

Zhaph - Ben Duguid
quelle
6
"1 Zeichen könnte durch 1 oder mehr Bytes dargestellt werden", stimme ich zu. Ich möchte nur diese Bytes, unabhängig davon, in welcher Codierung sich die Zeichenfolge befindet. Die einzige Möglichkeit, eine Zeichenfolge im Speicher zu speichern, sind Bytes. Gerade Zeichen werden als 1 oder mehr Bytes gespeichert. Ich möchte nur meine Bytes in die Hände bekommen.
Agnel Kurian
16
Sie benötigen die Codierungen nur, wenn Sie (oder eine andere Person) tatsächlich beabsichtigen, die Daten zu interpretieren , anstatt sie als generischen "Byteblock" zu behandeln. Für Dinge wie Komprimierung, Verschlüsselung usw. ist es bedeutungslos, sich Gedanken über die Codierung zu machen. In meiner Antwort finden Sie eine Möglichkeit, dies zu tun, ohne sich um die Codierung kümmern zu müssen.
user541686
9
@Mehrdad - Total, aber die ursprüngliche Frage, wie sie bei meiner ersten Beantwortung angegeben wurde, hat nicht vorgebeugt, was OP mit diesen Bytes passieren würde, nachdem sie konvertiert wurden, und für zukünftige Suchende sind die Informationen dazu relevant - dies ist Von Joels Antwort ganz gut abgedeckt - und wie Sie in Ihrer Antwort angeben: Vorausgesetzt, Sie bleiben in der .NET-Welt und verwenden Ihre Methoden zum Konvertieren von / nach, sind Sie glücklich. Sobald Sie darüber hinausgehen, spielt die Codierung eine Rolle.
Zhaph - Ben Duguid
Ein Codepunkt kann durch bis zu 4 Bytes dargestellt werden. (Eine UTF-32-Codeeinheit, ein UTF-16-Ersatzpaar oder 4 Bytes UTF-8.) Die Werte, für die UTF-8 mehr als 4 Bytes benötigen würde, liegen außerhalb des Unicode-Bereichs 0x0..0x10FFFF. ;-)
DevSolar
89

Dies ist eine beliebte Frage. Es ist wichtig zu verstehen, was der Autor der Frage stellt und dass es sich von dem unterscheidet, was wahrscheinlich am häufigsten benötigt wird. Um den Missbrauch des Codes dort zu verhindern, wo er nicht benötigt wird, habe ich den späteren zuerst beantwortet.

Gemeinsames Bedürfnis

Jede Zeichenfolge hat einen Zeichensatz und eine Codierung. Wenn Sie ein System.StringObjekt in ein Array von konvertieren, System.Bytehaben Sie immer noch einen Zeichensatz und eine Codierung. Für die meisten Anwendungen wissen Sie, welchen Zeichensatz und welche Codierung Sie benötigen, und .NET macht es einfach, "mit Konvertierung zu kopieren". Wählen Sie einfach die entsprechende EncodingKlasse.

// 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 in der Quelle unterstützt. Sie haben einige Mö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"

Conversions sind natürlich nicht unbedingt verlustfrei!

Hinweis: System.StringDer Quellzeichensatz ist Unicode.

Das einzig Verwirrende ist, dass .NET den Namen eines Zeichensatzes für den Namen einer bestimmten Codierung dieses Zeichensatzes verwendet. Encoding.Unicodesollte aufgerufen werden Encoding.UTF16.

Das war's für die meisten Verwendungen. Wenn Sie das brauchen, hören Sie hier auf zu lesen. Lesen Sie den lustigen Artikel von Joel Spolsky, wenn Sie nicht verstehen, was eine Codierung ist.

Spezifischer Bedarf

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

Er will keine Bekehrung.

Aus der C # -Spezifikation :

Die Zeichen- und Zeichenfolgenverarbeitung in C # verwendet die Unicode-Codierung. Der Zeichentyp repräsentiert eine UTF-16-Codeeinheit, und der Zeichenfolgentyp repräsentiert eine Folge von UTF-16-Codeeinheiten.

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

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

Aber um die Erwähnung von Kodierungen 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 nicht den gewünschten Datentyp, aber Mehrdads Antwort zeigt, wie dieses Char-Array mit BlockCopy in ein Byte-Array konvertiert wird . Dies kopiert die Zeichenfolge jedoch zweimal! Außerdem wird explizit codierungsspezifischer Code verwendet: der Datentyp System.Char.

Der einzige Weg, um zu den tatsächlichen Bytes zu gelangen, in denen der String gespeichert ist, ist die Verwendung eines Zeigers. Die fixedAnweisung ermöglicht die Angabe der Adresse von Werten. Aus der C # -Spezifikation:

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

Zu diesem Zweck schreibt der Compiler Code, der die anderen Teile des Zeichenfolgenobjekts mit überspringt RuntimeHelpers.OffsetToStringData. Um die Rohbytes 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 hervorhob, hängt das Ergebnis von der Endianness der Maschine ab. Aber der Fragesteller befasst sich nicht damit.

Tom Blodget
quelle
3
@Jan Das stimmt, aber die Stringlänge gibt bereits die Anzahl der Codeeinheiten an (keine Codepunkte).
Tom Blodget
1
Vielen Dank für den Hinweis! Von MSDN: "Die LengthEigenschaft [von String] gibt die Anzahl der CharObjekte in dieser Instanz zurück, nicht die Anzahl der Unicode-Zeichen." Ihr Beispielcode ist daher wie geschrieben korrekt.
Jan Hettich
1
@supercat "Der Zeichentyp stellt eine UTF-16-Codeeinheit dar, und der Zeichenfolgentyp stellt eine Folge von UTF-16-Codeeinheiten dar." —_ C # 5 Spezifikation._ Obwohl ja, nichts hindert eine ungültige Unicode-Zeichenfolge:new String(new []{'\uD800', '\u0030'})
Tom Blodget
1
@TomBlodget: Interessanterweise ist das Aufrufen der resultierenden Zeichenfolgen wesentlich schneller als das Aufrufen der Instanzen von oder , wenn man Instanzen von nimmt Globalization.SortKey, KeyDatadie resultierenden Bytes extrahiert und die resultierenden Bytes von jedem in ein String[zwei Bytes pro Zeichen, MSB zuerst ] packt sogar diese Instanzen aufrufen . Da frage ich mich , warum eine zurückkehrt , anstatt ein ? String.CompareOrdinalSortKey.CompareSortKeymemcmpKeyDataByte[]String
Supercat
1
Leider wird die richtige Antwort, aber Jahre zu spät, niemals so viele Stimmen haben wie die akzeptierten. Aufgrund von TL; DR werden die Leute denken, dass die akzeptierte Antwort rockt. kopieren und abstimmen.
Martin Capodici
46

Der erste Teil Ihrer Frage (wie man die Bytes erhält) wurde bereits von anderen beantwortet: Schauen Sie in den System.Text.EncodingNamespace.

Ich werde auf Ihre Folgefrage eingehen: Warum müssen Sie eine Codierung auswählen? Warum können Sie das nicht von der String-Klasse selbst bekommen?

Die Antwort besteht aus zwei Teilen.

Erstens spielen die von der Zeichenfolgenklasse intern verwendeten Bytes keine Rolle , und wann immer Sie davon ausgehen, dass dies der Fall ist, führen Sie wahrscheinlich einen Fehler ein.

Wenn sich Ihr Programm vollständig in der .NET-Welt befindet, müssen Sie sich keine Gedanken darüber machen, ob Sie Byte-Arrays für Zeichenfolgen erhalten, selbst wenn Sie Daten über ein Netzwerk senden. Verwenden Sie stattdessen .Net Serialization, um sich Gedanken über die Übertragung der Daten zu machen. Sie kümmern sich nicht mehr um die tatsächlichen Bytes: Der Serialisierungsformatierer erledigt dies für Sie.

Was ist andererseits, wenn Sie diese Bytes an einen Ort senden, von dem Sie nicht garantieren können, dass sie Daten aus einem serialisierten .Net-Stream abrufen? In diesem Fall müssen Sie sich definitiv um die Codierung kümmern, da sich dieses externe System offensichtlich darum kümmert. Auch hier spielen die von der Zeichenfolge verwendeten internen Bytes keine Rolle: Sie müssen eine Codierung auswählen, damit Sie diese Codierung auf der Empfangsseite explizit angeben können, selbst wenn es sich um dieselbe Codierung handelt, die intern von .Net verwendet wird.

Ich verstehe, dass Sie in diesem Fall möglicherweise die tatsächlichen Bytes verwenden möchten, die von der Zeichenfolgenvariablen im Speicher gespeichert werden, wenn dies möglich ist, mit der Idee, dass dadurch möglicherweise etwas Arbeit beim Erstellen Ihres Bytestreams gespart wird. Ich sage es Ihnen jedoch, es ist einfach nicht wichtig, um sicherzustellen, dass Ihre Ausgabe am anderen Ende verstanden wird, und um sicherzustellen, dass Sie mit Ihrer Codierung explizit sein müssen . Wenn Sie wirklich mit Ihren internen Bytes übereinstimmen möchten, können Sie bereits die UnicodeCodierung auswählen und so Leistungseinsparungen erzielen.

Das bringt mich zum zweiten Teil ... die Kommissionierung UnicodeCodierung wird sagen , .Net , die zugrunde liegende Bytes zu verwenden. Sie müssen diese Codierung auswählen, da die .Net-Laufzeit frei sein muss, um dieses neuere, bessere Codierungsmodell zu verwenden, ohne Ihr Programm zu beschädigen, wenn ein neues Unicode-Plus herauskommt. Für den Moment (und die absehbare Zukunft) erhalten Sie jedoch nur das, was Sie möchten, wenn Sie nur die Unicode-Codierung auswählen.

Es ist auch wichtig zu verstehen, dass Ihre Zeichenfolge neu in Wire geschrieben werden muss, und dies beinhaltet zumindest eine gewisse Übersetzung des Bitmusters, selbst wenn Sie eine passende Codierung verwenden . Der Computer muss Dinge wie Big vs Little Endian, Netzwerkbyte-Reihenfolge, Paketierung, Sitzungsinformationen usw. berücksichtigen.

Joel Coehoorn
quelle
9
Es gibt Bereiche in .NET, in denen Sie Byte-Arrays für Zeichenfolgen abrufen müssen. Viele der .NET Cryptrography-Klassen enthalten Methoden wie ComputeHash (), die Byte-Arrays oder Streams akzeptieren. Sie haben keine andere Wahl, als zuerst eine Zeichenfolge in ein Byte-Array zu konvertieren (Auswahl einer Codierung) und diese dann optional in einen Stream einzuschließen. Solange Sie jedoch eine Codierung (dh UTF8) für einen Stick auswählen, gibt es damit keine Probleme.
Ash
44

Um zu demonstrieren, dass Mehrdrads fundierte Antwort funktioniert, kann sein Ansatz sogar die ungepaarten Ersatzzeichen beibehalten (von denen sich viele gegen meine Antwort geeinigt hatten, von denen jedoch alle gleichermaßen schuldig sind, z. B. System.Text.Encoding.UTF8.GetByteskönnen System.Text.Encoding.Unicode.GetBytesdiese Codierungsmethoden das hohe Ersatzzeichen nicht beibehalten Zeichen d800zum Beispiel, und diese ersetzen lediglich 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

Versuchen Sie dies mit System.Text.Encoding.UTF8.GetBytes oder System.Text.Encoding.Unicode.GetBytes . Sie ersetzen lediglich hohe Ersatzzeichen durch den Wert fffd

Jedes Mal, wenn sich diese Frage bewegt, denke ich immer noch an einen Serializer (sei es von Microsoft oder von einer Komponente eines Drittanbieters), der Zeichenfolgen beibehalten kann, selbst wenn er ungepaarte Ersatzzeichen enthält. Ich google dies ab und zu: Serialisierung ungepaartes Ersatzzeichen .NET . Das lässt mich keinen Schlaf verlieren, aber es ist irgendwie ärgerlich, wenn ab und zu jemand meine Antwort kommentiert, dass sie fehlerhaft ist, aber ihre Antworten sind ebenso fehlerhaft, wenn es um ungepaarte Ersatzcharaktere geht.

Verdammt, Microsoft hätte gerade System.Buffer.BlockCopyin seinem BinaryFormatterツ verwenden sollen

谢谢!

Michael Buen
quelle
3
Müssen Ersatzzeichen nicht paarweise erscheinen, um gültige Codepunkte zu bilden? Wenn das der Fall ist, kann ich verstehen, warum die Daten entstellt würden.
Dtanders
1
@dtanders Ja, das sind auch meine Gedanken, sie müssen paarweise erscheinen, ungepaarte Ersatzzeichen kommen nur vor, wenn Sie sie absichtlich auf eine Schnur setzen und ungepaart machen. Was ich nicht weiß, ist, warum andere Entwickler immer wieder darauf hinweisen, dass wir stattdessen einen codierungsbewussten Ansatz verwenden sollten, da sie der Ansicht sind, dass der Serialisierungsansatz ( meine Antwort , die mehr als 3 Jahre lang akzeptiert wurde) den ungepaarten nicht hält Ersatzcharakter intakt. Aber sie vergaßen zu überprüfen, ob ihre codierungsbewussten Lösungen nicht auch den ungepaarten Ersatzcharakter, die Ironie, bewahren
Michael
Wenn es eine Serialisierungsbibliothek gibt, die System.Buffer.BlockCopyintern verwendet wird, sind alle Argumente der Kodierungsanwälte umstritten
Michael Buen,
2
@MichaelBuen Es scheint mir, dass das Hauptproblem darin besteht, dass Sie in großen, fetten Buchstaben sagen, dass etwas keine Rolle spielt, anstatt zu sagen, dass es in ihrem Fall keine Rolle spielt. Infolgedessen ermutigen Sie Leute, die sich Ihre Antwort ansehen, grundlegende Programmierfehler zu machen, die in Zukunft andere frustrieren werden. Ungepaarte Surrogate sind in einer Zeichenfolge ungültig. Da es sich nicht um ein char-Array handelt, ist es sinnvoll, dass das Konvertieren einer Zeichenfolge in ein anderes Format zu einem Fehler FFFDbei diesem Zeichen führt. Wenn Sie eine manuelle Zeichenfolgenmanipulation durchführen möchten, verwenden Sie wie empfohlen ein Zeichen [].
Trisped
2
@dtanders: A System.Stringist eine unveränderliche Folge von Char; .NET hat immer zugelassen, dass ein StringObjekt aus einem beliebigen Objekt erstellt Char[]und sein Inhalt in ein Objekt exportiert wird Char[], das dieselben Werte enthält, auch wenn das Original Char[]ungepaarte Ersatzzeichen enthält.
Supercat
41

Versuchen Sie dies, viel weniger Code:

System.Text.Encoding.UTF8.GetBytes("TEST String");
Nathan
quelle
Dann versuchen Sie es System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);und weinen Sie! Es wird funktionieren, aber System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Lengthwährend"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
mg30rg
9
@ mg30rg: Warum denkst du, ist dein Beispiel seltsam? Sicherlich haben bei einer Codierung mit variabler Breite nicht alle Zeichen die gleichen Bytelängen. Was stimmt damit nicht?
Vlad
@Vlad Ein gültiger Kommentar hier ist allerdings, dass als codierte Unicode - Zeichen (so, wie Bytes), Zeichen , die umfassen ihre eigenen diakritischen Zeichen werden ein anderes Ergebnis geben als diakritische Zeichen in Modifikator Symbole abspalten hinzugefügt den Charakter. In .net gibt es jedoch Methoden, um diese spezifisch abzuspalten und eine konsistente Bytedarstellung zu erhalten.
Nyerguds
25

Nun, ich habe alle Antworten gelesen und es ging um die Verwendung von Codierung oder um Serialisierung, bei der ungepaarte Surrogate gelöscht werden.

Es ist schlecht, wenn die Zeichenfolge beispielsweise von SQL Server stammt, wo sie aus einem Byte-Array erstellt wurde, in dem beispielsweise ein Kennwort-Hash gespeichert ist. Wenn wir etwas daraus löschen, wird ein ungültiger Hash gespeichert, und wenn wir ihn in XML speichern möchten, möchten wir ihn intakt lassen (da der XML-Writer eine Ausnahme für jeden gefundenen ungepaarten Ersatz löscht).

In solchen Fällen verwende ich die Base64- Codierung von Byte-Arrays, aber hey, im Internet gibt es nur eine Lösung für dieses Problem in C #, und es enthält einen Fehler und ist nur eine Möglichkeit. Deshalb habe ich den Fehler behoben und zurückgeschrieben 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);
}
Gman
quelle
Anstatt Ihre benutzerdefinierte Methode zum Konvertieren eines Byte-Arrays in base64 zu verwenden, mussten Sie lediglich den integrierten Konverter verwenden: Convert.ToBase64String (arr);
Makotosan
@ Makotosan danke, aber ich habe Convert.ToBase64String(arr); für die base64-Konvertierungen verwendet byte[] (data) <-> string (serialized data to store in XML file). Aber um die Initiale byte[] (data)zu erhalten, musste ich etwas mit einem machen String, das Binärdaten enthielt (so hat MSSQL es mir zurückgegeben). SO sind die obigen Funktionen für String (binary data) <-> byte[] (easy accessible binary data).
Gman
23

Bitte erläutern Sie auch, warum die Codierung berücksichtigt werden sollte. Kann ich nicht einfach abrufen, in welchen Bytes die Zeichenfolge gespeichert wurde? Warum diese Abhängigkeit von der Codierung? !!!

Weil es so etwas wie "die Bytes der Zeichenfolge" nicht gibt.

Eine Zeichenfolge (oder allgemeiner ein Text) besteht aus Zeichen: Buchstaben, Ziffern und anderen Symbolen. Das ist alles. Computer wissen jedoch nichts über Zeichen; Sie können nur Bytes verarbeiten. Wenn Sie Text mithilfe eines Computers speichern oder übertragen möchten, müssen Sie die Zeichen in Bytes umwandeln. Wie machst du das? Hier kommen Kodierungen ins Spiel.

Eine Codierung ist nichts anderes als eine Konvention, um logische Zeichen in physische Bytes zu übersetzen. Die einfachste und bekannteste Codierung ist ASCII, und es ist alles, was Sie brauchen, wenn Sie auf Englisch schreiben. Für andere Sprachen benötigen Sie vollständigere Codierungen, da jede der Unicode-Varianten heutzutage die sicherste Wahl ist.

Kurz gesagt, der Versuch, "die Bytes einer Zeichenfolge ohne Verwendung von Codierungen abzurufen", ist ebenso unmöglich wie "das Schreiben eines Textes ohne Verwendung einer Sprache".

Übrigens empfehle ich Ihnen (und jedem anderen) dringend, dieses kleine Stück Weisheit zu lesen: Das absolute Minimum, das jeder Softwareentwickler unbedingt über Unicode- und Zeichensätze wissen muss (keine Ausreden!)

Konamiman
quelle
2
Lassen Sie mich klarstellen: Eine Codierung wurde verwendet, um "Hallo Welt" in physische Bytes zu übersetzen. Da die Zeichenfolge auf meinem Computer gespeichert ist, muss sie sicher in Bytes gespeichert werden. Ich möchte lediglich auf diese Bytes zugreifen, um sie auf der Festplatte oder aus einem anderen Grund zu speichern. Ich möchte diese Bytes nicht interpretieren. Da ich diese Bytes nicht interpretieren möchte, ist die Notwendigkeit einer Codierung an dieser Stelle ebenso fehl am Platz wie die Notwendigkeit einer Telefonleitung, um printf aufzurufen.
Agnel Kurian
3
Aber auch hier gibt es kein Konzept für die Übersetzung von Text in physische Bytes, es sei denn, Sie verwenden eine Codierung. Sicher, der Compiler speichert die Zeichenfolgen irgendwie im Speicher - aber es wird nur eine interne Codierung verwendet, die Sie (oder jemand außer dem Compiler-Entwickler) nicht kennen. Was auch immer Sie tun, Sie benötigen eine Codierung, um physische Bytes aus einer Zeichenfolge abzurufen.
Konamiman
@Agnel Kurian: Es ist natürlich wahr, dass ein String irgendwo eine Reihe von Bytes hat, die seinen Inhalt speichern (UTF-16 afair). Es gibt jedoch einen guten Grund, Sie daran zu hindern, darauf zuzugreifen: Zeichenfolgen sind unveränderlich, und wenn Sie das interne Byte [] -Array erhalten könnten, könnten Sie es auch ändern. Dies unterbricht die Unveränderlichkeit, was wichtig ist, da mehrere Zeichenfolgen dieselben Daten gemeinsam nutzen können. Wenn Sie eine UTF-16-Codierung verwenden, um die Zeichenfolge abzurufen, werden die Daten wahrscheinlich nur kopiert.
ollb
2
@ Gnafoo, Eine Kopie der Bytes reicht aus.
Agnel Kurian
22

C #, um a stringin ein byteArray zu konvertieren :

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
Shyam Sundar Shah
quelle
17
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
gkrogers
quelle
Aber warum sollte die Codierung berücksichtigt werden? Warum kann ich die Bytes nicht einfach abrufen, ohne sehen zu müssen, welche Codierung verwendet wird? Sollte das String-Objekt selbst dann nicht wissen, welche Codierung verwendet wird, wenn es erforderlich wäre, und einfach den Speicherauszug sichern?
Agnel Kurian
5
Das funktioniert nicht immer. Einige Sonderzeichen können bei einer solchen Methode verloren gehen, die ich auf die harte Tour gefunden habe.
JB King
17

Sie können den folgenden Code für die Konvertierung zwischen Zeichenfolge 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);
Jarvis Stark
quelle
VUPdies hat mein Problem gelöst (byte [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd
16

Mit dem Aufkommen der Span<T>Veröffentlichung mit C # 7.2 lautet die kanonische Technik zum Erfassen der zugrunde liegenden Speicherdarstellung eines Strings in einem verwalteten Byte-Array:

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

Das Zurückkonvertieren sollte kein Starter sein, da dies bedeutet, dass Sie die Daten tatsächlich irgendwie interpretieren, aber der Vollständigkeit halber:

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

Die Namen NonPortableCastund DangerousGetPinnableReferencesollten das Argument fördern, dass Sie dies wahrscheinlich nicht tun sollten.

Beachten Sie, dass für die Arbeit mit Span<T>das System.Memory NuGet-Paket installiert werden muss .

Unabhängig davon implizieren die eigentliche ursprüngliche Frage und die nachfolgenden Kommentare, dass der zugrunde liegende Speicher nicht "interpretiert" wird (was meiner Meinung nach bedeutet, dass das Mittel nicht geändert oder gelesen wird, ohne dass es so geschrieben werden muss, wie es ist), was auf eine gewisse Implementierung der StreamKlasse hinweist sollte verwendet werden, anstatt über die Daten als Zeichenfolgen nachzudenken.

John Rasch
quelle
13

Ich bin nicht sicher, aber ich denke, der String speichert seine Informationen als Array von Zeichen, was mit Bytes ineffizient ist. Insbesondere lautet die Definition eines Zeichens "Repräsentiert ein Unicode-Zeichen".

Nehmen Sie dieses Beispielbeispiel:

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

Beachten Sie, dass die Unicode-Antwort in beiden Fällen 14 Byte beträgt, während die UTF-8-Antwort für die erste nur 9 Byte und für die zweite nur 7 Byte beträgt.

Wenn Sie also nur die von der Zeichenfolge verwendeten Bytes möchten, verwenden Sie einfach Encoding.Unicode, aber der Speicherplatz ist ineffizient.

Ed Marty
quelle
10

Das Hauptproblem besteht darin, dass eine Glyphe in einer Zeichenfolge 32 Bit benötigt (16 Bit für einen Zeichencode), ein Byte jedoch nur 8 Bit übrig hat. Eine Eins-zu-Eins-Zuordnung gibt es nur, wenn Sie sich auf Zeichenfolgen beschränken, die nur ASCII-Zeichen enthalten. System.Text.Encoding bietet viele Möglichkeiten, eine Zeichenfolge Byte [] zuzuordnen. Sie müssen eine auswählen, die Informationsverlust vermeidet und von Ihrem Client einfach verwendet werden kann, wenn er das Byte [] wieder einer Zeichenfolge zuordnen muss .

Utf8 ist eine beliebte Codierung, kompakt und nicht verlustbehaftet.

Hans Passant
quelle
3
UTF-8 ist nur kompakt, wenn sich die meisten Ihrer Zeichen im englischen Zeichensatz (ASCII) befinden. Wenn Sie eine lange Zeichenfolge chinesischer Zeichen hätten, wäre UTF-16 eine kompaktere Codierung als UTF-8 für diese Zeichenfolge. Dies liegt daran, dass UTF-8 ein Byte zum Codieren von ASCII verwendet und andernfalls 3 (oder vielleicht 4).
Joel Mueller
7
Wahr. Aber wie können Sie nichts über Codierung wissen, wenn Sie mit dem Umgang mit chinesischem Text vertraut sind?
Hans Passant
9

Verwenden:

    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
Mashet
quelle
OP bittet ausdrücklich darum, KEINE Codierung anzugeben ... "ohne manuell eine bestimmte Codierung anzugeben"
Ferdz
8

Schnellste Weg

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

EDIT als Makotosan kommentierte dies ist jetzt die beste Art und Weise:

Encoding.UTF8.GetBytes(text)
Alessandro Annini
quelle
8
ASCIIEncoding ..... wird nicht benötigt. Die einfache Verwendung von Encoding.UTF8.GetBytes (Text) wird bevorzugt.
Makotosan
8

Wie konvertiere ich eine Zeichenfolge in .NET (C #) in ein Byte [], ohne manuell eine bestimmte Codierung anzugeben?

Eine Zeichenfolge in .NET stellt Text als Folge von UTF-16-Codeeinheiten dar, sodass die Bytes bereits in UTF-16 im Speicher codiert sind.

Mehrdads Antwort

Sie können Mehrdads Antwort verwenden , aber es wird tatsächlich eine Codierung verwendet, da die Zeichen UTF-16 sind. Es ruft ToCharArray auf, das beim Betrachten der Quelle ein erstellt char[]und den Speicher direkt darauf kopiert. Anschließend werden die Daten in ein Byte-Array kopiert, das ebenfalls zugewiesen ist. Unter der Haube werden also die zugrunde liegenden Bytes zweimal kopiert und ein char-Array zugewiesen, das nach dem Aufruf nicht verwendet wird.

Tom Blodgets Antwort

Die Antwort von Tom Blodget ist 20 bis 30% schneller als die von Mehrdad, da der Zwischenschritt des Zuweisens eines char-Arrays und des Kopierens der Bytes übersprungen wird. Sie müssen jedoch mit der /unsafeOption kompilieren . Wenn Sie absolut keine Codierung verwenden möchten, ist dies meiner Meinung nach der richtige Weg. Wenn Sie Ihr Verschlüsselungs-Login in den fixedBlock einfügen, müssen Sie nicht einmal ein separates Byte-Array zuweisen und die Bytes darauf kopieren.

Warum sollte auch die Kodierung berücksichtigt werden? Kann ich nicht einfach abrufen, in welchen Bytes die Zeichenfolge gespeichert wurde? Warum besteht eine Abhängigkeit von Zeichenkodierungen?

Weil das der richtige Weg ist. stringist eine Abstraktion.

Die Verwendung einer Codierung kann zu Problemen führen, wenn Sie Zeichenfolgen mit ungültigen Zeichen haben. Dies sollte jedoch nicht passieren. Wenn Sie Daten mit ungültigen Zeichen in Ihre Zeichenfolge aufnehmen, machen Sie es falsch. Sie sollten wahrscheinlich zunächst ein Byte-Array oder eine Base64-Codierung verwenden.

Wenn Sie verwenden System.Text.Encoding.Unicode, ist Ihr Code widerstandsfähiger. Sie müssen sich keine Sorgen um die Endianness machen des Systems machen, auf dem Ihr Code ausgeführt wird. Sie müssen sich keine Sorgen machen, wenn die nächste Version der CLR eine andere interne Zeichenkodierung verwendet.

Ich denke, die Frage ist nicht, warum Sie sich um die Codierung kümmern möchten, sondern warum Sie sie ignorieren und etwas anderes verwenden möchten. Die Codierung soll die Abstraktion einer Zeichenfolge in einer Folge von Bytes darstellen. System.Text.Encoding.UnicodeSie erhalten eine kleine Codierung der Endian-Byte-Reihenfolge und können dies jetzt und in Zukunft auf jedem System tun.

Jason Goemaat
quelle
Tatsächlich ist eine Zeichenfolge in C # NICHT nur auf UTF-16 beschränkt. Was wahr ist, ist, dass es einen Vektor von 16-Bit-Codeeinheiten enthält, aber diese 16-Bit-Codeeinheiten sind nicht auf gültiges UTF-16 beschränkt. Da es sich jedoch um 16-Bit handelt, benötigen Sie eine Codierung (Bytereihenfolge), um sie in 8-Bit zu konvertieren. Eine Zeichenfolge kann dann Nicht-Unicode-Daten speichern, einschließlich Binärcode (z. B. ein Bitmap-Bild). Es wird nur in E / A- und Textformatierern, die eine solche Interpretation vornehmen, als UTF-16 interpretiert.
verdy_p
In einer C # -String können Sie also eine Codeeinheit wie 0xFFFF oder 0xFFFE sicher speichern, auch wenn sie in UTF-16 keine Zeichen sind, und Sie können eine isolierte 0xD800 speichern, gefolgt von einer Codeeinheit in 0xDC00..0xDFFF (dh ungepaarte Surrogate, die in UTF-16 ungültig sind). Die gleiche Bemerkung gilt für Zeichenfolgen in Javascript / ECMAscript und Java.
verdy_p
Wenn Sie "GetBytes" verwenden, geben Sie natürlich keine Codierung an, sondern nehmen eine Bytereihenfolge an, um die beiden Bytes in einer Spezifikation für jede lokal in der Zeichenfolge gespeicherte Codeeinheit abzurufen. Wenn Sie eine neue Zeichenfolge aus Bytes erstellen, benötigen Sie auch einen Konverter, nicht unbedingt UTF-8 zu UTF-16. Sie können die zusätzliche 0 in das High-Byte einfügen oder zwei Bytes (in MSB first oder LSB first order) einpacken die gleiche 16-Bit-Codeeinheit. Strings sind dann kompakte Form für Arrays von 16-Bit-Ganzzahlen. Die Beziehung zu "Zeichen" ist ein weiteres Problem, in C # sind sie keine tatsächlichen Typen, da sie immer noch als Zeichenfolgen dargestellt werden
verdy_p
7

Der Ansatz, der dem OP am nächsten kommt, ist der von Tom Blodget, der tatsächlich in das Objekt geht und die Bytes extrahiert. Ich sage am nächsten, weil es von der Implementierung des String-Objekts abhängt.

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

Sicher, aber hier entsteht der grundlegende Fehler in der Frage. Der String ist ein Objekt, das eine interessante Datenstruktur haben könnte. Wir wissen es bereits, weil es ermöglicht, ungepaarte Surrogate zu speichern. Es könnte die Länge speichern. Es könnte einen Zeiger auf jeden der 'gepaarten' Surrogate behalten, was ein schnelles Zählen ermöglicht. Alle diese zusätzlichen Bytes sind nicht Teil der Zeichendaten.

Was Sie wollen, sind die Bytes jedes Zeichens in einem Array. Und hier kommt die 'Codierung' ins Spiel. Standardmäßig erhalten Sie UTF-16LE. Wenn Sie sich nicht für die Bytes selbst interessieren, außer für den Roundtrip, können Sie eine beliebige Codierung einschließlich der 'Standardcodierung' auswählen und später wieder konvertieren (unter der Annahme derselben Parameter wie Standardcodierung, Codepunkte und Fehlerkorrekturen) , Dinge erlaubt wie ungepaarte Leihmütter, etc.

Aber warum sollte man die "Kodierung" der Magie überlassen? Warum nicht die Codierung angeben, damit Sie wissen, welche Bytes Sie erhalten werden?

"Why is there a dependency on character encodings?"

Codierung (in diesem Zusammenhang) bedeutet einfach die Bytes, die Ihre Zeichenfolge darstellen. Nicht die Bytes des String-Objekts. Sie wollten die Bytes, in denen die Zeichenfolge gespeichert wurde - hier wurde die Frage naiv gestellt. Sie wollten die Zeichenfolgenbytes in einem zusammenhängenden Array, das die Zeichenfolge darstellt, und nicht alle anderen Binärdaten, die ein Zeichenfolgenobjekt enthalten kann.

Was bedeutet, wie eine Zeichenfolge gespeichert wird, ist irrelevant. Sie möchten eine Zeichenfolge, die in Bytes in einem Byte-Array "codiert" ist.

Ich mag die Antwort von Tom Bloget, weil er Sie in Richtung "Bytes des String-Objekts" geführt hat. Es hängt jedoch von der Implementierung ab, und da er sich Interna ansieht, kann es schwierig sein, eine Kopie der Zeichenfolge wiederherzustellen.

Mehrdads Antwort ist falsch, weil sie auf konzeptioneller Ebene irreführend ist. Sie haben noch eine Liste von codierten Bytes. Seine spezielle Lösung ermöglicht es, ungepaarte Surrogate zu erhalten - dies ist implementierungsabhängig. Seine spezielle Lösung würde die Bytes der Zeichenfolge nicht genau erzeugen, wenn GetBytesdie Zeichenfolge standardmäßig in UTF-8 zurückgegeben würde.


Ich habe meine Meinung dazu geändert (Mehrdads Lösung) - dies bekommt nicht die Bytes der Zeichenfolge; Vielmehr werden die Bytes des Zeichenarrays abgerufen, das aus der Zeichenfolge erstellt wurde. Unabhängig von der Codierung hat der Datentyp char in c # eine feste Größe. Dies ermöglicht die Erzeugung eines Byte-Arrays mit konsistenter Länge und die Wiedergabe des Zeichen-Arrays basierend auf der Größe des Byte-Arrays. Wenn also die Codierung UTF-8 wäre, aber jedes Zeichen 6 Byte groß wäre, um den größten utf8-Wert aufzunehmen, würde es immer noch funktionieren. In der Tat spielt die Kodierung des Zeichens keine Rolle.

Es wurde jedoch eine Konvertierung verwendet - jedes Zeichen wurde in ein Feld mit fester Größe (Zeichentyp von c #) eingefügt. Was diese Darstellung ist, spielt jedoch keine Rolle, was technisch die Antwort auf das OP ist. Also - wenn Sie trotzdem konvertieren wollen ... Warum nicht "codieren"?

Gerard ONeill
quelle
Diese Zeichen werden von UTF-8 oder UTF-16 oder sogar UTF-32 nicht unterstützt, zum Beispiel: 񩱠& (Char) 55906& (Char) 55655. Sie können sich also irren und Mehrdads Antwort ist eine sichere Konvertierung, ohne zu berücksichtigen, welche Art von Codierungen verwendet werden.
Mojtaba Rezaeian
Raymon, die Zeichen werden bereits durch einen Unicode-Wert dargestellt - und alle Unicode-Werte können durch alle utfs dargestellt werden. Gibt es eine längere Erklärung dafür, wovon Sie sprechen? In welcher Zeichenkodierung existieren diese beiden Werte (oder 3 ..)?
Gerard ONeill
Dies sind ungültige Zeichen, die von keinem Codierungsbereich unterstützt werden. Dies bedeutet nicht, dass sie zu 100% nutzlos sind. Ein Code, der jede Art von Zeichenfolge unabhängig von der Codierung in ein Byte-Array-Äquivalent konvertiert, ist überhaupt keine falsche Lösung und hat bei gewünschten Gelegenheiten seine eigene Verwendung.
Mojtaba Rezaeian
1
Ok, dann denke ich, dass Sie das Problem nicht verstehen. Wir wissen, dass es sich um ein Unicode-kompatibles Array handelt. Da es sich um .net handelt, wissen wir, dass es sich um UTF-16 handelt. Diese Charaktere werden dort also nicht existieren. Sie haben auch meinen Kommentar zur Änderung interner Darstellungen nicht vollständig gelesen. Ein String ist ein Objekt, kein codiertes Byte-Array. Also werde ich Ihrer letzten Aussage nicht zustimmen. Sie möchten, dass Code alle Unicode-Zeichenfolgen in eine beliebige UTF-Codierung konvertiert. Dies macht, was Sie wollen, richtig.
Gerard ONeill
Objekte sind eine Folge von Daten, ursprünglich eine Folge von Bits, die ein Objekt in seinem aktuellen Zustand beschreiben. Daher können alle Daten in Programmiersprachen in ein Array von Bytes konvertiert werden (jedes Byte definiert 8 Bits), da Sie möglicherweise einen bestimmten Status eines Objekts im Speicher behalten müssen. Sie können eine Folge von Bytes in einer Datei oder einem Speicher speichern und speichern und sie nach dem Lesen von der Festplatte als Ganzzahl, Bigint, Bild, ASCII-Zeichenfolge, UTF-8-Zeichenfolge, verschlüsselte Zeichenfolge oder Ihren eigenen definierten Datentyp umwandeln. Man kann also nicht sagen, dass Objekte etwas anderes sind als die Bytesequenz.
Mojtaba Rezaeian
6

Sie können den folgenden Code verwenden, um stringa byte arrayin .NET zu konvertieren

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
Shyam Sundar Shah
quelle
3

Wenn Sie wirklich eine Kopie der zugrunde liegenden Bytes einer Zeichenfolge möchten, können Sie eine Funktion wie die folgende verwenden. Sie sollten jedoch nicht weiterlesen, 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 ziemlich schnell eine Kopie der Bytes, die Ihrer Zeichenfolge zugrunde liegen. Sie erhalten diese Bytes auf jede Art und Weise, die sie auf Ihrem System codieren. Diese Codierung ist mit ziemlicher Sicherheit UTF-16LE, aber das ist ein Implementierungsdetail, um das Sie sich nicht kümmern sollten.

Es wäre sicherer, einfacher und zuverlässiger , einfach anzurufen.

System.Text.Encoding.Unicode.GetBytes()

Höchstwahrscheinlich führt dies zum gleichen Ergebnis, ist einfacher zu tippen und die Bytes werden immer mit einem Aufruf an umgeschaltet

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

Hier ist meine unsichere Umsetzung Stringzu Byte[]konvertieren:

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

Es ist viel schneller als das der akzeptierten Antwort, auch wenn es nicht so elegant ist wie es ist. Hier sind meine Stoppuhr-Benchmarks über 10000000 Iterationen:

[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

Um es zu verwenden, müssen Sie in Ihren Projekterstellungseigenschaften "Unsicheren Code zulassen" ankreuzen. Gemäß .NET Framework 3.5 kann diese Methode auch als Zeichenfolgenerweiterung verwendet werden:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
Tommaso Belluzzo
quelle
Ist der Wert RuntimeHelpers.OffsetToStringDataeines Vielfachen von 8 in den Itanium-Versionen von .NET? Andernfalls schlägt dies aufgrund der nicht ausgerichteten Lesevorgänge fehl.
Jon Hanna
wäre es nicht einfacher aufzurufen memcpy? stackoverflow.com/a/27124232/659190
Jodrell
2

Verwenden Sie einfach dies:

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
Alireza Amini
quelle
2
... und alle Charaktere mit einem Sprung über 127 verlieren. In meiner Muttersprache ist es vollkommen gültig, "Árvíztűrő tükörfúrógép." zu schreiben. System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();gibt "Árvizturo tukörfurogép."verlorene Informationen zurück, die nicht abgerufen werden können. (Und ich habe noch keine asiatischen Sprachen erwähnt, in denen Sie alle Zeichen
verlieren
2

Die Zeichenfolge kann aus folgenden Gründen auf verschiedene Weise in ein Byte-Array konvertiert werden: .NET unterstützt Unicode, und Unicode standardisiert mehrere Differenzcodierungen, die als UTFs bezeichnet werden. Sie haben unterschiedliche Längen der Bytedarstellung, sind jedoch in dem Sinne äquivalent, dass eine Zeichenfolge, wenn sie codiert wird, zurück in die Zeichenfolge codiert werden kann. Wenn die Zeichenfolge jedoch mit einer UTF codiert und unter der Annahme einer anderen UTF decodiert wird, wenn sie geschraubt werden kann oben.

Außerdem unterstützt .NET Nicht-Unicode-Codierungen, diese sind jedoch im Allgemeinen nicht gültig (nur gültig, wenn eine begrenzte Teilmenge des Unicode-Codepunkts in einer tatsächlichen Zeichenfolge wie ASCII verwendet wird). Intern unterstützt .NET UTF-16, für die Stream-Darstellung wird jedoch normalerweise UTF-8 verwendet. Es ist auch ein Standard für das Internet.

Es überrascht nicht, dass die Serialisierung von Zeichenfolgen in ein Array von Bytes und die Deserialisierung von der Klasse unterstützt wird System.Text.Encoding, die eine abstrakte Klasse ist. Die abgeleiteten Klassen unterstützen konkrete Codierungen: ASCIIEncodingund vier UTFs ( System.Text.UnicodeEncodingunterstützt UTF-16)

Ref diesen Link.

Zur Serialisierung in ein Array von Bytes mit System.Text.Encoding.GetBytes. Für die inverse Operation verwenden System.Text.Encoding.GetChars. Diese Funktion gibt ein Array von Zeichen zurück. Um eine Zeichenfolge abzurufen, verwenden Sie einen Zeichenfolgenkonstruktor System.String(char[]).
Ref diese Seite.

Beispiel:

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
quelle
2

Es hängt davon ab, was Sie die Bytes für wollen

Dies liegt daran, wie Tyler so treffend sagte : "Strings sind keine reinen Daten. Sie haben auch Informationen ." In diesem Fall handelt es sich bei den Informationen um eine Codierung, die beim Erstellen der Zeichenfolge angenommen wurde.

Angenommen, Sie haben Binärdaten (anstelle von Text) in einer Zeichenfolge gespeichert

Dies basiert auf dem Kommentar von OP zu seiner eigenen Frage und ist die richtige Frage, wenn ich die Hinweise von OP auf den Anwendungsfall verstehe.

Das Speichern von Binärdaten in Strings ist aufgrund der oben erwähnten angenommenen Codierung wahrscheinlich der falsche Ansatz! Welches Programm oder welche Bibliothek diese Binärdaten in einem string(anstelle eines byte[]besser geeigneten Arrays) gespeichert hat, hat den Kampf bereits verloren, bevor er begonnen hat. Wenn sie die Bytes in einer REST-Anfrage / Antwort oder irgendetwas, das Zeichenfolgen übertragen muss, an Sie senden, wäre Base64 der richtige Ansatz.

Wenn Sie eine Textzeichenfolge mit einer unbekannten Codierung haben

Alle anderen haben diese falsche Frage falsch beantwortet.

Wenn die Zeichenfolge so wie sie ist gut aussieht, wählen Sie einfach eine Codierung aus (vorzugsweise eine, die mit UTF beginnt), verwenden Sie die entsprechende System.Text.Encoding.???.GetBytes()Funktion und teilen Sie mit, wem Sie die Bytes für die ausgewählte Codierung geben.

NH.
quelle
2

Auf die Frage, was Sie mit den Bytes machen wollen, antworteten Sie :

Ich werde es verschlüsseln. Ich kann es verschlüsseln, ohne es zu konvertieren, aber ich möchte trotzdem wissen, warum hier die Codierung zum Tragen kommt. Gib mir einfach die Bytes, sage ich.

Unabhängig davon, ob Sie diese verschlüsselten Daten über das Netzwerk senden, später wieder in den Speicher laden oder auf einen anderen Prozess übertragen möchten, beabsichtigen Sie eindeutig, sie irgendwann zu entschlüsseln . In diesem Fall lautet die Antwort, dass Sie ein Kommunikationsprotokoll definieren. Ein Kommunikationsprotokoll sollte nicht definiert in Bezug auf den Details der Implementierung Ihrer Programmiersprache und die damit verbundene Laufzeit. Dafür gibt es mehrere Gründe:

  • Möglicherweise müssen Sie mit einem Prozess kommunizieren, der in einer anderen Sprache oder Laufzeit implementiert ist. (Dies kann beispielsweise einen Server umfassen, der auf einem anderen Computer ausgeführt wird oder die Zeichenfolge an einen JavaScript-Browser-Client sendet.)
  • Das Programm wird möglicherweise in Zukunft in einer anderen Sprache oder Laufzeit erneut implementiert.
  • Die .NET-Implementierung kann die interne Darstellung von Zeichenfolgen ändern. Sie mögen denken, dass dies weit hergeholt klingt, aber dies geschah tatsächlich in Java 9 , um die Speichernutzung zu reduzieren. Es gibt keinen Grund, warum .NET diesem Beispiel nicht folgen könnte. Skeet schlägt vor, dass UTF-16 heute wahrscheinlich nicht optimal ist, was dazu führt, dass das Emoji und andere Unicode-Blöcke ebenfalls mehr als 2 Bytes zur Darstellung benötigen, was die Wahrscheinlichkeit erhöht, dass sich die interne Darstellung in Zukunft ändern könnte.

Für die Kommunikation (entweder mit einem völlig unterschiedlichen Prozess oder mit demselben Programm in der Zukunft) müssen Sie Ihr Protokoll streng definieren, um die Schwierigkeit zu minimieren, damit zu arbeiten oder versehentlich Fehler zu verursachen. Abhängig von der internen Darstellung von .NET ist dies keine strikte, klare oder sogar garantiert konsistente Definition. Eine Standardkodierung ist eine strenge Definition, die Sie in Zukunft nicht enttäuschen wird.

Mit anderen Worten, Sie können Ihre Anforderungen an die Konsistenz nicht erfüllen, ohne eine Codierung anzugeben.

Sie können sich sicherlich für die direkte Verwendung von UTF-16 entscheiden, wenn Sie feststellen, dass Ihr Prozess eine erheblich bessere Leistung erbringt, da .NET ihn intern oder aus einem anderen Grund verwendet. Sie müssen diese Codierung jedoch explizit auswählen und diese Konvertierungen explizit in Ihrem Code durchführen, anstatt davon abhängig zu sein zur internen Implementierung von .NET.

Wählen Sie also eine Codierung und verwenden Sie sie:

using System.Text;

// ...

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

Wie Sie sehen können, ist es auch weniger Code, nur die integrierten Codierungsobjekte zu verwenden, als Ihre eigenen Lese- / Schreibmethoden zu implementieren.

jpmc26
quelle
1

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, die untere öfter als die obere zu verwenden, habe sie nicht auf Geschwindigkeit gemessen.


quelle
4
Was ist mit Multibyte-Zeichen?
Agnel Kurian
c.ToByte () ist privat: S
Khodor
@AgnelKurian Msdn sagt: "Diese Methode gibt einen vorzeichenlosen Bytewert zurück, der den numerischen Code des an sie übergebenen Char-Objekts darstellt. In .NET Framework ist ein Char-Objekt ein 16-Bit-Wert. Dies bedeutet, dass die Methode für die Rückgabe geeignet ist Die numerischen Codes der Zeichen im ASCII-Zeichenbereich oder in den Bereichen Unicode C0 Controls und Basic Latin sowie C1 Controls und Latin-1 Supplement reichen von U + 0000 bis 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
user1120193
quelle