it-swarm.it

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

Come faccio a convertire un string in 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 personaggi?

2055
Agnel Kurian

Contrariamente alle risposte qui, NON è necessario preoccuparsi della codifica se i byte non devono essere interpretati!

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

Per quegli obiettivi, onestamente faccio non capisco 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 cercano in qualche modo interpretare i byte, che ovviamente non hai menzionato tu intendi fare, allora c'è niente sbagliato in questo approccio ! Preoccuparsi delle codifiche ti rende la vita più complicata senza un vero motivo.

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é tu sei solo guardando i byte .

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

1793
Mehrdad

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 conta:

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, il framework .NET usa UTF-16 per rappresentare le stringhe, quindi se vuoi semplicemente ottenere i byte esatti che usa .NET, usa System.Text.Encoding.Unicode.GetBytes (...).

VediCodifica caratteri in .NET Framework(MSDN) per ulteriori informazioni.

1079
bmotmans

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 ...

267
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());
110
Michael Buen

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

Joel ha un post su questo:

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

87

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 oggetto System.String in un array di System.Byte hai ancora un set di caratteri e una codifica. Per la maggior parte degli usi, sapresti quale set di caratteri e codifica ti servono e .NET semplifica la "copia con conversione". Basta scegliere la classe Encoding appropriata.

// 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 personaggio 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.Guarda l'articolo divertente Joel Spolsky se non capisci cos'è una codifica.

Bisogno specifico

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

Lui non vuole alcuna conversione.

Dalla specifica C # :

L'elaborazione di caratteri e stringhe in C # utilizza la codifica Unicode. Il tipo di carattere rappresenta un'unità di codice UTF-16 e il tipo di stringa rappresenta una 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 Mehrdad's answer mostra come convertire questo array Char in un array Byte usando BlockCopy . Tuttavia, questo copia la stringa due volte! Inoltre, utilizza esplicitamente un 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. L'istruzione fixed consente di prendere l'indirizzo dei valori. Dalla specifica C #:

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

Per fare ciò, il compilatore scrive il codice salta sulle altre parti dell'oggetto stringa con 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 funzionalità della macchina. Ma l'autore della domanda non si occupa di questo.

84
Tom Blodget

Solo per dimostrare che il suono di Mehrdrad answer funziona, il suo approccio può persino persistere nel caratteri surrogati non appaiati (di cui molti hanno messo a confronto la mia risposta, ma di cui tutti sono ugualmente colpevoli, ad esempio System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; quei metodi di codifica non possono mantenere i caratteri surrogati alti d800 per esempio, e quelli semplicemente sostituiscono i caratteri surrogati alti con il 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

Provalo con System.Text.Encoding.UTF8.GetBytes or 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 stringhe anche se contiene caratteri surrogati non accoppiati; 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.

Dannazione, Microsoft avrebbe dovuto usare System.Buffer.BlockCopy nel suo BinaryFormatter

谢谢!

43
Michael Buen

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

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 hanno importanza , e ogni volta che si presume lo facciano probabilmente introducendo un bug.

Se il tuo programma è interamente nel 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 selezionare 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. Comunque, te l'ho messo, non è importante rispetto a fare in modo che il tuo output sia compreso dall'altra parte, e per garantire che tu must sia esplicito con la tua codifica. Inoltre, se vuoi davvero abbinare i tuoi byte interni, puoi già scegliere la codifica Unicodename__, e ottenere così un risparmio di prestazioni.

Il che mi porta alla seconda parte ... selezionare la codifica Unicodename__ is telling .Net per 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ù nuovo 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 su filo, e ciò implica almeno una certa traduzione di bit-pattern anche quando si utilizza una codifica corrispondente . Il computer deve tenere conto di cose come Big vs Little Endian, ordine dei byte di rete, packetization, informazioni sulla sessione, ecc.

42
Joel Coehoorn

Prova questo, molto meno codice:

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

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

È brutto quando la stringa, ad esempio, proviene da SQL Server in cui è stata creata 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 spaiato che trova).

Quindi io uso Base64 codifica di array di byte in questi casi, ma ciao, su Internet c'è una sola soluzione a questo in C #, e ha bug in esso ed è solo un modo, quindi ho risolto il problema bug e procedura scritta indietro. 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);
}
24
Gman

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 qualsiasi dei gusti 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 positivamente Unicode e set di caratteri (nessuna scusa!)

21
Konamiman

C # per convertire un string in un array byte:

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

È possibile utilizzare il seguente codice per la conversione tra array di stringhe e byte.

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);
16
Jarvis Stark
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
15
gkrogers

Non sono sicuro, ma penso che la stringa memorizzi le sue informazioni come una serie di caratteri, che è inefficiente con i byte. Nello specifico, la definizione di Char è "Rappresenta un carattere Unicode".

prendi questo esempio di esempio:

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

Prendi nota che la risposta Unicode è 14 byte in entrambe le istanze, mentre la risposta UTF-8 è solo 9 byte per la prima e solo 7 per la seconda.

Quindi, se vuoi solo i byte usati dalla stringa, usa semplicemente Encoding.Unicode, ma sarà inefficiente con lo spazio di archiviazione.

12
Ed Marty

Con l'avvento di Span<T> rilasciato con C # 7.2, la tecnica canonica per catturare la rappresentazione della memoria sottostante di una stringa in un array di byte gestito è:

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

La conversione indietro dovrebbe essere un non-starter perché significa che in effetti stai interpretando i dati in qualche modo, ma per completezza:

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

I nomi NonPortableCast e DangerousGetPinnableReference dovrebbero sostenere l'argomento che probabilmente non dovresti fare questo.

Nota che lavorare con Span<T> richiede l'installazione del pacchetto System.Memory NuGet .

Indipendentemente da ciò, la domanda actual original e i commenti di follow-up implicano che la memoria sottostante non viene "interpretata" (che presumo che i mezzi non siano modificati o letti oltre la necessità di scriverlo così com'è), indicando dovrebbe essere usata qualche implementazione della classe Stream invece di ragionare sui dati come stringhe.

11
John Rasch

Il problema chiave è che un glifo in una stringa accetta 32 bit (16 bit per un codice carattere) ma un byte ha solo 8 bit da risparmiare. Una mappatura uno-a-uno non esiste a meno che non ti limiti alle stringhe che contengono solo caratteri ASCII. System.Text.Encoding ha molti modi per mappare una stringa in byte [], devi sceglierne una che eviti la perdita di informazioni e che sia facile da usare dal tuo client quando deve mappare il byte [] su una stringa .

Utf8 è una codifica popolare, è compatta e non lossy.

10
Hans Passant

Uso:

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

Il risultato è:

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

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

A string in .NET rappresenta il testo come una sequenza di unità di codice UTF-16, quindi i byte sono già codificati in memoria in UTF-16.

La risposta di Mehrdad

Puoi usare la risposta di Mehrdad , ma in realtà usa una codifica perché i caratteri sono UTF-16. Chiama ToCharArray che guarda a source crea un char[] e copia direttamente la memoria su di esso. Quindi copia i dati in un array di byte che viene anche allocato. Quindi sotto la cappa sta copiando i byte sottostanti due volte e allocando un array di caratteri che non viene utilizzato dopo la chiamata.

Risposta di Tom Blodget

La risposta di Tom Blodget è 20-30% più veloce di Mehrdad poiché salta il passaggio intermedio dell'allocazione di un array di caratteri e copia i byte su di esso, ma richiede la compilazione con l'opzione /unsafe. Se non vuoi assolutamente usare la codifica, penso che questa sia la strada da percorrere. Se si inserisce il login di crittografia nel blocco fixed, non è nemmeno necessario allocare un array di byte separato e copiare i byte su di esso.

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 personaggi?

Perché quello è il modo giusto per farlo. string è un'astrazione.

L'utilizzo di una codifica potrebbe causare problemi se si dispone di "stringhe" con caratteri non validi, ma ciò non dovrebbe accadere. Se stai ricevendo dati nella tua stringa con caratteri non validi, stai sbagliando. Probabilmente dovresti usare una matrice di byte o una codifica Base64 per iniziare.

Se usi System.Text.Encoding.Unicode, il tuo codice sarà più resistente. Non devi preoccuparti di endianness del sistema su cui verrà eseguito il tuo codice. Non è necessario preoccuparsi se la prossima versione di CLR utilizzerà una codifica di caratteri interni diversa.

Penso che la domanda non sia perché vuoi preoccuparti della codifica, ma perché vuoi ignorarla e usare qualcos'altro. La codifica è intesa per rappresentare l'astrazione di una stringa in una sequenza di byte. System.Text.Encoding.Unicode ti darà una piccola codifica per l'ordine dei byte endian e funzionerà allo stesso modo su tutti i sistemi, ora e in futuro.

8
Jason Goemaat

Il modo più veloce

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

EDITcome Makotosan ha commentato questo è ora il modo migliore:

Encoding.UTF8.GetBytes(text)
8
Sunrising

L'approccio più vicino alla domanda dell'OP è di Tom Blodget, che in realtà entra nell'oggetto ed estrae i byte. Dico più vicino perché dipende dall'implementazione dell'oggetto String.

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

Certo, ma è qui che sorge l'errore fondamentale nella domanda. La stringa è un oggetto che potrebbe avere una struttura di dati interessante. Lo sappiamo già, perché consente di archiviare i surrogati non appaiati. Potrebbe memorizzare la lunghezza. Potrebbe mantenere un puntatore a ciascuno dei surrogati 'accoppiati' consentendo il conteggio veloce. Ecc. Tutti questi byte extra non fanno parte dei dati dei personaggi.

Quello che vuoi sono i byte di ogni carattere in una matrice. Ed è qui che entra in gioco la "codifica". Per impostazione predefinita si ottiene UTF-16LE. Se non ti preoccupi dei byte stessi tranne per il round trip, puoi scegliere qualsiasi codifica che includa il 'default', e ricontrollarla più tardi (assumendo gli stessi parametri come quello che era la codifica predefinita, code point, correzioni di bug , cose permesse come surrogati spaiati, ecc.

Ma perché lasciare la "codifica" alla magia? Perché non specificare la codifica in modo da sapere quali byte si otterranno?

"Why is there a dependency on character encodings?"

Codifica (in questo contesto) significa semplicemente i byte che rappresentano la tua stringa. Non i byte dell'oggetto stringa. Volevi i byte in cui è stata memorizzata la stringa - questo è dove la domanda è stata posta in modo ingenuo. Volevi i byte di stringa in una matrice contigua che rappresenta la stringa e non tutti gli altri dati binari che un oggetto stringa può contenere.

Il che significa che una stringa è archiviata è irrilevante. Vuoi una stringa "codificata" in byte in un array di byte.

Mi piace la risposta di Tom Bloget perché ti ha indirizzato verso la direzione "byte dell'oggetto stringa". Tuttavia, dipende dall'implementazione e, dato che sta dando una occhiata agli interni, potrebbe essere difficile ricostituire una copia della stringa.

La risposta di Mehrdad è sbagliata perché è fuorviante a livello concettuale. Hai ancora una lista di byte, codificati. La sua particolare soluzione consente di preservare surrogati inaccoppiati - questo dipende dall'implementazione. La sua soluzione particolare non produrrebbe i byte della stringa con precisione se GetBytes restituisse la stringa in UTF-8 per impostazione predefinita.


Ho cambiato idea su questo (la soluzione di Mehrdad) - questo non sta ottenendo i byte della stringa; piuttosto sta ottenendo i byte dell'array di caratteri che è stato creato dalla stringa. Indipendentemente dalla codifica, il tipo di dati char in c # è una dimensione fissa. Ciò consente di produrre una matrice di byte di lunghezza coerente e consente la riproduzione dell'array di caratteri in base alle dimensioni dell'array di byte. Quindi, se la codifica fosse UTF-8, ma ogni char fosse 6 byte per contenere il valore utf8 più grande, funzionerebbe comunque. Quindi, in effetti, la codifica del personaggio non ha importanza.

Ma è stata utilizzata una conversione: ogni personaggio è stato inserito in una casella di dimensioni fisse (tipo di carattere di c #). Tuttavia ciò che questa rappresentazione non ha importanza, che è tecnicamente la risposta al PO. Quindi, se hai intenzione di convertire comunque ... Perché non 'codificare'?

6
Gerard ONeill

È possibile utilizzare il seguente codice per convertire un string in byte array in .NET

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
6

Ecco la mia implementazione non sicura della conversione da String a Byte[]:

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

È molto più veloce di quella accettata, anche se non è elegante come è. Ecco i miei benchmark di cronometro su oltre 10000000 iterazioni:

[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

Per usarlo, devi spuntare "Consenti codice non sicuro" nelle proprietà di costruzione del tuo progetto. Secondo .NET Framework 3.5, questo metodo può essere utilizzato anche come estensione String:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
4
Tommaso Belluzzo

Se vuoi veramente una copia dei byte sottostanti di una stringa, puoi usare una funzione come quella che segue. Tuttavia, non dovresti ti preghiamo di leggere per scoprire perché.

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

Questa funzione ti porterà una copia dei byte sottostanti la tua stringa, abbastanza velocemente. Otterrai quei byte in qualunque modo codificano sul tuo sistema. Questa codifica è quasi certamente UTF-16LE ma è un dettaglio di implementazione che non dovresti avere a cuore.

Sarebbe più sicuro, più semplice e più affidabile solo per chiamare,

System.Text.Encoding.Unicode.GetBytes()

Con ogni probabilità questo darà lo stesso risultato, è più facile da digitare, ei byte saranno sempre round-trip con una chiamata a

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

Basta usare questo:

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
3
alireza amini

La stringa può essere convertita in array di byte in vari modi, a causa del fatto seguente: .NET supporta Unicode e Unicode standardizza diverse codifiche di differenze chiamate UTF. Hanno lunghezze diverse di rappresentazione dei byte ma sono equivalenti nel senso che quando una stringa è codificata, può essere ricondotta alla stringa, ma se la stringa è codificata con un UTF e decodificata nell'ipotesi di UTF diverso se può essere avvitata su.

Inoltre, .NET supporta codifiche non Unicode, ma non sono valide in generale (sarà valido solo se un sottoinsieme limitato di punto di codice Unicode viene utilizzato in una stringa effettiva, come ASCII). Internamente, .NET supporta UTF-16, ma per la rappresentazione del flusso, UTF-8 viene solitamente utilizzato. È anche uno standard di fatto per Internet.

Non sorprende che la serializzazione della stringa in una matrice di byte e deserializzazione sia supportata dalla classe System.Text.Encoding, che è una classe astratta; le sue classi derivate supportano le codifiche concrete: ASCIIEncoding e quattro UTF (System.Text.UnicodeEncoding supporta UTF-16)

Rif questo link.

Per la serializzazione su una matrice di byte usando System.Text.Encoding.GetBytes. Per l'operazione inversa utilizzare System.Text.Encoding.GetChars. Questa funzione restituisce una matrice di caratteri, quindi per ottenere una stringa, utilizzare un costruttore di stringhe System.String(char[]).
Rif. Questa pagina.

Esempio:

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? :-)
2
Vijay Singh Rana

Dipende da ciò che vuoi per i byte

Questo perché, come Tyler è così giustamente detto , "Le stringhe non sono dati puri. Hanno anche informazioni ". In questo caso, l'informazione è una codifica che è stata assunta quando la stringa è stata creata.

Supponendo che si disponga di dati binari (anziché di testo) memorizzati in una stringa

Questo si basa sul commento di OP sulla sua stessa domanda, ed è la domanda corretta se capisco i suggerimenti di OP al caso d'uso.

Memorizzare i dati binari nelle stringhe è probabilmente l'approccio sbagliato a causa della presunta codifica sopra menzionata! Qualunque programma o libreria abbiano memorizzato i dati binari in un string (invece di un array byte[] che sarebbe stato più appropriato) ha già perso la battaglia prima che sia iniziata. Se inviano loro i byte in una REST richiesta/risposta o qualcosa che deve trasmettere stringhe, Base64 sarebbe l'approccio giusto.

Se hai una stringa di testo con una codifica sconosciuta

Tutti gli altri hanno risposto in modo errato a questa domanda errata.

Se la stringa sembra buona così com'è, basta scegliere una codifica (preferibilmente una che inizia con UTF), utilizzare la funzione System.Text.Encoding.???.GetBytes() corrispondente e dire a chi si assegnano i byte a quale codifica hai scelto.

2
NH.

codice semplice con LINQ

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

EDIT: come commentato di seguito, non è un buon modo.

ma puoi ancora usarlo per capire LINQ con una codifica più appropriata:

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

Due strade:

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

E,

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

Tendo ad usare quello in basso più spesso rispetto al top, non li ho benchmark per la velocità.

2
harmonik
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
2
user1120193

Un personaggio è sia una chiave di ricerca in una tabella di caratteri sia una tradizione lessicale come l'ordinamento, le versioni maiuscole e minuscole, ecc.

Di conseguenza, un carattere non è un byte (8 bit) e un byte non è un carattere. In particolare, le 256 permutazioni di un byte non possono contenere migliaia di simboli all'interno di alcune lingue scritte, molto meno tutte le lingue. Quindi, sono stati ideati vari metodi per codificare i caratteri. Alcuni codificano per una particolare classe di linguaggi (codifica ASCII); più lingue usando le code page (Extended ASCII); o, ambiziosamente, tutti i linguaggi, includendo selettivamente byte aggiuntivi secondo necessità, Unicode.

All'interno di un sistema, come il framework .NET, una stringa implica una particolare codifica dei caratteri. In .NET questa codifica è Unicode. Poiché il framework legge e scrive Unicode per impostazione predefinita, in genere la codifica dei caratteri non è necessaria in .NET.

Tuttavia, in generale, per caricare una stringa di caratteri nel sistema da un flusso di byte è necessario conoscere la codifica sorgente per interpretarla e successivamente tradurla correttamente (altrimenti i codici saranno presi come già nella codifica predefinita del sistema e quindi renderizzati senza senso). Allo stesso modo, quando una stringa viene scritta su una fonte esterna, sarà scritta in una particolare codifica.

0
George

Da byte[] a string:

        return BitConverter.ToString(bytes);
0
Piero Alberto

Ho scritto un'estensione di Visual Basic simile alla risposta accettata, ma utilizzando direttamente la memoria .NET e Marshalling per la conversione e supporta intervalli di caratteri non supportati in altri metodi, come UnicodeEncoding.UTF8.GetString o UnicodeEncoding.UTF32.GetString o addirittura MemoryStream and BinaryFormatter (caratteri non validi come: ???? & ChrW(55906) & ChrW(55655)):

<Extension> _
Public Function ToBytesMarshal(ByRef str As String) As Byte()
    Dim gch As GCHandle = GCHandle.Alloc(str, GCHandleType.Pinned)
    Dim handle As IntPtr = gch.AddrOfPinnedObject
    ToBytesMarshal = New Byte(str.Length * 2 - 1) {}
    Try
        For i As Integer = 0 To ToBytesMarshal.Length - 1
            ToBytesMarshal.SetValue(Marshal.ReadByte(IntPtr.Add(handle, i)), i)
        Next
    Finally
        gch.Free()
    End Try
End Function

<Extension> _
Public Function ToStringMarshal(ByRef arr As Byte()) As String
    Dim gch As GCHandle = GCHandle.Alloc(arr, GCHandleType.Pinned)
    Try
        ToStringMarshal = Marshal.PtrToStringAuto(gch.AddrOfPinnedObject)
    Finally
        gch.Free()
    End Try
End Function
0
Mojtaba Rezaeian