Vraag Hoe krijg ik een consistente byte-weergave van strings in C # zonder handmatig een codering op te geven?


Hoe converteer ik een string naar een byte[] in .NET (C #) zonder handmatig een specifieke codering op te geven?

Ik ga de string versleutelen. Ik kan het coderen zonder te converteren, maar ik zou toch graag willen weten waarom de codering hier komt spelen.

En waarom zou codering in aanmerking moeten worden genomen? Kan ik niet eenvoudig krijgen naar welke bytes de string is opgeslagen? Waarom is er een afhankelijkheid van karaktercoderingen?


1909
2018-01-23 13:39


oorsprong


antwoorden:


In tegenstelling tot de antwoorden hier, hoeft u zich GEEN zorgen te maken over codering als de bytes hoeven niet geïnterpreteerd te worden!

Zoals je al zei, je doel is simpelweg om "krijg welke bytes de string is opgeslagen in".
(En natuurlijk om de reeks van de bytes opnieuw te construeren.)

Voor die doelen, ik eerlijk gezegd niet begrijp waarom mensen je blijven vertellen dat je de coderingen nodig hebt. U hoeft zich hier zeker geen zorgen over te maken.

Doe dit gewoon in plaats:

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

Zolang je programma (of andere programma's) dit niet proberen interpreteren de bytes op een of andere manier, waarvan je duidelijk niet vermeldde dat je het van plan bent te doen, dan is er dat wel niets verkeerd met deze aanpak! Piekeren over coderingen maakt je leven gewoon gecompliceerder zonder echte reden.

Bijkomend voordeel voor deze aanpak:

Het maakt niet uit of de string ongeldige tekens bevat, omdat je toch de gegevens kunt krijgen en toch de originele string kunt reconstrueren!

Het wordt net zo gecodeerd en gedecodeerd, omdat je dat bent gewoon kijken naar de bytes.

Als u echter een specifieke codering gebruikte, zou dit problemen hebben veroorzaakt bij het coderen / decoderen van ongeldige tekens.


1721
2018-04-30 07:44



Het hangt af van de codering van uw string (ASCII, UTF-8, ...).

Bijvoorbeeld:

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

Een kleine steekproef waarom encoding belangrijk is:

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 is eenvoudigweg niet uitgerust om met speciale karakters om te gaan.

Intern gebruikt het .NET-framework UTF-16 om strings voor te stellen, dus als je gewoon de exacte bytes wilt zien die .NET gebruikt, gebruik dan System.Text.Encoding.Unicode.GetBytes (...).

Zien Tekencodering in het .NET Framework (MSDN) voor meer informatie.


1052
2018-01-23 13:43



Het geaccepteerde antwoord is heel, heel ingewikkeld. Gebruik hiervoor de meegeleverde .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);

Stel het wiel niet opnieuw uit als u niet hoeft te ...


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



U moet rekening houden met de codering, omdat 1 teken kan worden weergegeven met 1 of meer bytes (tot ongeveer 6), en verschillende coderingen zullen deze bytes anders behandelen.

Joel heeft hierover een bericht:

Het absolute minimum Elke software-ontwikkelaar Absoluut, positief moet het weten over Unicode- en tekensets (geen excuses!)


79
2018-01-23 14:03



Dit is een populaire vraag. Het is belangrijk om te begrijpen wat de vraag van de auteur is en dat deze anders is dan waarschijnlijk de meest voorkomende. Om misbruik van de code te ontmoedigen waar dat niet nodig is, heb ik later de eerste beantwoord.

Gemeenschappelijke behoefte

Elke string heeft een tekenset en codering. Wanneer u een converteert System.String bezwaar maken tegen een array van System.Byte je hebt nog steeds een tekenset en codering. Voor de meeste toepassingen weet u welke tekenset en codering u nodig hebt. NET maakt het eenvoudig om 'te kopiëren met conversie'. Kies gewoon het juiste Encoding klasse.

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

De conversie moet mogelijk worden afgehandeld wanneer de doelkarakterset of -codering geen karakter ondersteunt dat zich in de bron bevindt. Je hebt een aantal keuzes: uitzondering, vervanging of overslaan. Het standaardbeleid is om een ​​'?' Te vervangen.

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

Het is duidelijk dat conversies niet noodzakelijk zonder verlies zijn!

Opmerking: voor System.String de bron-tekenset is Unicode.

Het enige verwarrende is dat .NET de naam van een tekenset gebruikt voor de naam van een bepaalde codering van die tekenset. Encoding.Unicode zou moeten worden gebeld Encoding.UTF16.

Dat is het voor de meeste gebruiken. Als dat is wat je nodig hebt, stop dan met lezen hier. Zie het plezier Joel Spolsky-artikel als je niet begrijpt wat een codering is.

Specifieke behoefte

Nu vraagt ​​de vraag aan de auteur: "Elke string wordt opgeslagen als een array van bytes, toch? Waarom kan ik die bytes gewoon niet hebben?"

Hij wil geen conversie.

Van de C # spec:

Karakter- en stringverwerking in C # maakt gebruik van Unicode-codering. De char   type staat voor een UTF-16-code-eenheid en het tekenreekstype staat voor a   volgorde van UTF-16 code-eenheden.

We weten dus dat als we om de nulomzetting vragen (d.w.z. van UTF-16 tot UTF-16), we het gewenste resultaat krijgen:

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

Maar om de vermelding van coderingen te vermijden, moeten we het op een andere manier doen. Als een tussentijds gegevenstype acceptabel is, bestaat hiervoor een conceptuele snelkoppeling:

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

Dat levert ons niet het gewenste datatype op, maar Mehrdad's antwoord laat zien hoe je deze Char-array naar een Byte-array converteert met BlockCopy. Dit kopieert de string echter twee keer! En het gebruikt ook expliciet coderingsspecifieke code: het datatype System.Char.

De enige manier om bij de eigenlijke bytes te komen waarin de String is opgeslagen, is om een ​​aanwijzer te gebruiken. De fixed verklaring maakt het mogelijk om het adres van waarden te nemen. Van de C # -specificatie:

[Voor] een expressie van het type string, ... de initializer berekent de   adres van het eerste teken in de tekenreeks.

Om dit te doen, schrijft de compiler-code code over de andere delen van het string-object met RuntimeHelpers.OffsetToStringData. Dus, om de onbewerkte bytes te krijgen, maakt u gewoon een aanwijzer naar de tekenreeks en kopieert u het benodigde aantal 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;
}

Zoals @CodesInChaos opmerkte, hangt het resultaat af van de endianness van de machine. Maar de vraagauteur houdt zich daar niet mee bezig.


76
2017-12-02 04:43



Gewoon om dat geluid van Mehrdrad te demonstreren antwoord werkt, zijn aanpak kan zelfs de ongepaarde surrogate karakters(waarvan velen zich hadden genesteld tegen mijn antwoord, maar waar iedereen even schuldig aan is, bijvoorbeeld System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; die coderingsmethoden kunnen de hoge surrogaatkarakters niet aanhouden d800bijvoorbeeld, en die vervangen gewoonweg hoge surrogaatkarakters door waarde 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);
    }        
}

Output:

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

Probeer dat eens met System.Text.Encoding.UTF8.GetBytes of System.Text.Encoding.Unicode.GetBytes, ze vervangen alleen hoge surrogaatkarakters met waarde FFFD

Elke keer dat er een beweging is in deze vraag, denk ik nog steeds aan een serializer (hetzij van Microsoft of een component van een derde partij) die de string kan behouden, zelfs als deze ongepaarde surrogaat-karakters bevat; Ik google dit zo nu en dan: serialisatie ongepaard surrogaat karakter .NET. Dit laat me geen slaap verliezen, maar het is een beetje vervelend als er zo nu en dan iemand reageert op mijn antwoord dat het gebrekkig is, maar hun antwoorden zijn even gebrekkig als het gaat om ongepaarde draagletters.

Verdorie, Microsoft had het net moeten gebruiken System.Buffer.BlockCopy in zijn BinaryFormatter ツ

谢谢!


35
2017-07-25 22:52