Domanda Come ottengo una rappresentazione di byte coerente di stringhe in C # senza specificare manualmente una codifica?


Come posso convertire a string a a byte[] in .NET (C #) senza specificare manualmente una codifica specifica?

Ho intenzione di crittografare la stringa. Posso crittografarlo senza convertirli, ma mi piacerebbe ancora sapere perché la codifica viene qui a giocare.

Inoltre, perché la codifica dovrebbe essere presa in considerazione? Non posso semplicemente ottenere in quale byte è stata memorizzata la stringa? Perché c'è una dipendenza dalle codifiche dei caratteri?


1909
2018-01-23 13:39


origine


risposte:


Contrariamente alle risposte qui, NON devi preoccuparti della codifica Se i byte non devono essere interpretati!

Come hai detto, il tuo obiettivo è, semplicemente, quello di "ottieni ciò che la stringa è stata memorizzata in".
(E, naturalmente, per essere in grado di ricostruire la stringa dai byte.)

Per quegli obiettivi, lo faccio onestamente non capisci perché la gente continua a dirti che hai bisogno delle codifiche. Sicuramente non devi preoccuparti delle codifiche per questo.

Basta fare questo invece:

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

Finché il tuo programma (o altri programmi) non ci provano interpretare i byte in qualche modo, che ovviamente non hai menzionato che intendi fare, poi c'è Niente sbagliato con questo approccio! Preoccuparsi delle codifiche ti rende la vita più complicata senza una vera ragione.

Ulteriori vantaggi per questo approccio:

Non importa se la stringa contiene caratteri non validi, perché puoi comunque ottenere i dati e ricostruire comunque la stringa originale!

Sarà codificato e decodificato allo stesso modo, perché lo sei solo guardando i byte.

Se hai usato una codifica specifica, però, ti avrebbe dato problemi con la codifica / decodifica di caratteri non validi.


1721
2018-04-30 07:44



Dipende dalla codifica della stringa (ASCII, UTF-8, ...).

Per esempio:

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

Un piccolo esempio per cui la codifica è importante:

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 semplicemente non è equipaggiato per trattare personaggi speciali.

Internamente, utilizza il framework .NET UTF-16 per rappresentare le stringhe, quindi se vuoi semplicemente ottenere i byte esatti che usa .NET, usa System.Text.Encoding.Unicode.GetBytes (...).

Vedere Codifica dei caratteri in .NET Framework (MSDN) per ulteriori informazioni.


1052
2018-01-23 13:43



La risposta accettata è molto, molto complicata. Utilizzare le classi .NET incluse per questo:

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

Non reinventare la ruota se non devi ...


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



È necessario prendere in considerazione la codifica, poiché 1 carattere potrebbe essere rappresentato da 1 o più byte (fino a circa 6) e le diverse codifiche tratteranno questi byte in modo diverso.

Joel ha un post su questo:

Il minimo assoluto Ogni sviluppatore di software deve assolutamente conoscere l'Unicode e i set di caratteri (nessuna scusa!)


79
2018-01-23 14:03



Questa è una domanda popolare. È importante capire che cosa chiede la domanda all'autore e che è diversa da quella che è probabilmente la necessità più comune. Per scoraggiare l'uso improprio del codice laddove non è necessario, ho risposto per primo.

Bisogno comune

Ogni stringa ha un set di caratteri e codifica. Quando converti un System.String oggetto a un array di System.Byte hai ancora un set di caratteri e la codifica. Per la maggior parte degli usi, sapresti quale set di caratteri e codifica hai bisogno e .NET semplifica la "copia con conversione". Basta scegliere l'appropriato Encoding classe.

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

La conversione potrebbe dover gestire casi in cui il set di caratteri di destinazione o la codifica non supportano un carattere presente nella fonte. Avete alcune scelte: eccezione, sostituzione o salto. La politica di default è di sostituire un '?'.

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

Chiaramente, le conversioni non sono necessariamente senza perdite!

Nota: per System.String il set di caratteri di origine è Unicode.

L'unica cosa confusa è che .NET usa il nome di un set di caratteri per il nome di una particolare codifica di quel set di caratteri. Encoding.Unicode dovrebbe essere chiamato Encoding.UTF16.

Questo è tutto per la maggior parte degli usi. Se è quello di cui hai bisogno, smetti di leggere qui. Vedi il divertimento Articolo di Joel Spolsky se non capisci cos'è una codifica.

Bisogno specifico

Ora, la domanda dell'autore chiede: "Ogni stringa è memorizzata come una matrice di byte, giusto? Perché non posso semplicemente avere quei byte?"

Lui non vuole alcuna conversione.

Dal Spec. C #:

L'elaborazione di caratteri e stringhe in C # utilizza la codifica Unicode. Il char   type rappresenta un'unità di codice UTF-16 e il tipo di stringa rappresenta a   sequenza di unità di codice UTF-16.

Quindi, sappiamo che se chiediamo la conversione nulla (cioè da UTF-16 a UTF-16), otterremo il risultato desiderato:

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

Ma per evitare la menzione delle codifiche, dobbiamo farlo in un altro modo. Se un tipo di dati intermedi è accettabile, esiste una scorciatoia concettuale per questo:

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

Questo non ci porta il tipo di dati desiderato ma La risposta di Mehrdad mostra come convertire questo array Char in un array Byte usando BlockCopy. Tuttavia, questo copia la stringa due volte! Inoltre, usa esplicitamente il codice specifico per la codifica: il tipo di dati System.Char.

L'unico modo per ottenere i byte effettivi in ​​cui è memorizzata la stringa è utilizzare un puntatore. Il fixed la dichiarazione consente di prendere l'indirizzo dei valori. Dalla specifica C #:

[Per] un'espressione di tipo stringa, ... l'inizializzatore calcola il   indirizzo del primo carattere nella stringa.

Per fare ciò, il compilatore scrive il codice salta sulle altre parti dell'oggetto stringa RuntimeHelpers.OffsetToStringData. Quindi, per ottenere i byte non elaborati, basta creare un puntatore alla stringa e copiare il numero di byte necessari.

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

Come sottolineato da @CodesInChaos, il risultato dipende dalla endianità della macchina. Ma l'autore della domanda non si occupa di questo.


76
2017-12-02 04:43



Solo per dimostrare che il suono di Mehrdrad risposta funziona, il suo approccio può anche persistere personaggi surrogati spaiati(di cui molti avevano livellato contro la mia risposta, ma di cui tutti sono ugualmente colpevoli, ad es. System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; quei metodi di codifica non possono mantenere i caratteri surrogati alti d800per esempio, e quelli semplicemente si limitano a sostituire alti caratteri surrogati con valore 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);
    }        
}

Produzione:

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

Prova con System.Text.Encoding.UTF8.GetBytes o System.Text.Encoding.Unicode.GetBytes, sostituiranno semplicemente alti caratteri surrogati con valore FFFD

Ogni volta che c'è un movimento in questa domanda, sto ancora pensando a un serializzatore (che si tratti di Microsoft o di un componente di terze parti) che possa persistere nelle stringhe anche se contiene caratteri surrogati non appaiati; Io google questo ogni tanto: serializzazione carattere surrogato non abbinato .NET. Questo non mi fa perdere il sonno, ma è un po 'fastidioso quando ogni tanto qualcuno commenta la mia risposta che è difettosa, eppure le loro risposte sono ugualmente imperfette quando si tratta di personaggi surrogati spaiati.

Maledizione, Microsoft dovrebbe aver appena usato System.Buffer.BlockCopy nel suo BinaryFormatter ツ

谢谢!


35
2017-07-25 22:52



Prova questo, molto meno codice:

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

34
2018-01-23 15:54



La prima parte della tua domanda (come ottenere i byte) ha già ricevuto risposta da altri: guarda nel System.Text.Encoding namespace.

Tratterò la tua domanda di follow-up: perché hai bisogno di scegliere una codifica? Perché non puoi ottenerlo dalla stessa classe di stringhe?

La risposta è in due parti.

Prima di tutto, i byte utilizzati internamente dalla classe string non importae ogni volta che pensi di farlo, probabilmente stai introducendo un bug.

Se il tuo programma è interamente all'interno del mondo .Net, allora non devi preoccuparti di ottenere array di byte per le stringhe, anche se stai inviando dati attraverso una rete. Invece, utilizzare la serializzazione .Net per preoccuparsi della trasmissione dei dati. Non ti preoccupare più dei byte effettivi: il formattatore di serializzazione lo fa per te.

D'altra parte, cosa succede se si inviano questi byte da qualche parte che non si può garantire l'estrazione dei dati da un flusso serializzato .Net? In questo caso hai sicuramente bisogno di preoccuparti della codifica, perché ovviamente questo sistema esterno si preoccupa. Quindi, di nuovo, i byte interni utilizzati dalla stringa non contano: è necessario scegliere una codifica in modo da poter essere espliciti su questa codifica sul lato ricevente, anche se è la stessa codifica utilizzata internamente da .Net.

Capisco che in questo caso potresti preferire utilizzare i byte effettivi memorizzati dalla variabile stringa nella memoria, ove possibile, con l'idea che potrebbe salvare del lavoro creando il flusso di byte. Tuttavia, te l'ho messo, non è importante rispetto al fatto che il tuo output sia compreso dall'altra parte, e per garantire che tu dovere sii esplicito con la tua codifica. Inoltre, se vuoi davvero abbinare i tuoi byte interni, puoi già scegliere il Unicode codifica e ottenere quel risparmio di prestazioni.

Il che mi porta alla seconda parte ... raccogliendo il Unicode codifica è dire a .Net di usare i byte sottostanti. È necessario scegliere questa codifica, perché quando esce Unicode-Plus un po 'nuovo, il runtime .Net deve essere libero di utilizzare questo modello di codifica più recente e migliore senza interrompere il programma. Ma, per il momento (e il futuro prevedibile), scegliere la codifica Unicode ti dà quello che vuoi.

È anche importante capire che la stringa deve essere riscritta sul filo, e ciò implica almeno una traduzione del pattern di bit anche quando si utilizza una codifica corrispondente. Il computer deve tenere conto di cose come Big vs Little Endian, ordine dei byte di rete, pacchetti, informazioni sulla sessione, ecc.


34
2018-03-10 08:57



Bene, ho letto tutte le risposte e stavano usando la codifica o uno sulla serializzazione che fa cadere surrogati inermi.

È brutto quando arriva la stringa, per esempio server SQL dove è stato costruito da un array di byte che memorizza, ad esempio, un hash della password. Se ne eliminiamo qualcosa, memorizzerà un hash non valido e, se vogliamo archiviarlo in XML, vogliamo lasciarlo intatto (perché il writer XML rilascia un'eccezione su qualsiasi surrogato non appaiato trovato).

Quindi io uso Base64 codifica di array di byte in questi casi, ma ciononostante, su Internet esiste una sola soluzione a questo in C #, e contiene un bug ed è solo un modo, quindi ho risolto il problema e ho scritto la procedura. Eccoti, futuri 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);
}

22
2017-07-16 11:45



Spiega anche perché la codifica deve essere presa in considerazione.   Non posso semplicemente ottenere in quale byte è stata memorizzata la stringa?   Perché questa dipendenza dalla codifica? !!!

Perché non esistono cose come "i byte della stringa".

Una stringa (o più genericamente, un testo) è composta da caratteri: lettere, cifre e altri simboli. È tutto. I computer, tuttavia, non sanno nulla dei personaggi; possono gestire solo byte. Pertanto, se si desidera archiviare o trasmettere il testo utilizzando un computer, è necessario trasformare i caratteri in byte. Come si fa a farlo? Ecco dove arrivano le codifiche sulla scena.

Una codifica non è altro che una convenzione per tradurre i caratteri logici in byte fisici. La codifica più semplice e più conosciuta è ASCII, ed è tutto ciò che serve se scrivi in ​​inglese. Per altre lingue avrete bisogno di codifiche più complete, essendo uno dei sapori Unicode la scelta più sicura al giorno d'oggi.

Quindi, in breve, cercare di "ottenere i byte di una stringa senza utilizzare le codifiche" è impossibile quanto "scrivere un testo senza usare alcun linguaggio".

A proposito, ti consiglio vivamente (e chiunque altro) di leggere questo piccolo pezzo di saggezza: Il minimo assoluto Ogni sviluppatore di software deve assolutamente conoscere l'Unicode e i set di caratteri (nessuna scusa!)


18
2018-06-05 10:52