Come posso ottenere una rappresentazione in byte coerente delle stringhe in C # senza specificare manualmente una codifica?


2190

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

Ho intenzione di crittografare la stringa. Posso crittografarlo senza convertirlo, ma mi piacerebbe comunque sapere perché la codifica viene riprodotta qui.

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


23
Ogni stringa è memorizzata come una matrice di byte, giusto? Perché non posso semplicemente avere quei byte?
Agnel Kurian,

135
La codifica è ciò che associa i caratteri ai byte. Ad esempio, in ASCII, la lettera "A" è mappata al numero 65. In una codifica diversa, potrebbe non essere la stessa. L'approccio di alto livello alle stringhe adottato nel framework .NET rende tuttavia questo in gran parte irrilevante (tranne in questo caso).
Lucas Jones,

20
Per giocare con l'avvocato del diavolo: se volevi ottenere i byte di una stringa in memoria (come li usa .NET) e manipolarli in qualche modo (cioè CRC32), e MAI MAI MAI MAI voluto decodificarlo nuovamente nella stringa originale ... non è chiaro il motivo per cui ti interesserebbe la codifica o come scegli quale utilizzare.
Greg,

79
Sorpreso nessuno ha ancora dato questo link: joelonsoftware.com/articles/Unicode.html
Bevan

28
Un char non è un byte e un byte non è un char. Un carattere è sia una chiave in una tabella dei caratteri che una tradizione lessicale. Una stringa è una sequenza di caratteri. (A parole, paragrafi, frasi e titoli hanno anche le loro tradizioni lessicali che giustificano le proprie definizioni di tipo - ma sto divagando). Come numeri interi, numeri in virgola mobile e tutto il resto, i caratteri sono codificati in byte. C'è stato un tempo in cui la codifica era semplice: ASCII. Tuttavia, per soddisfare tutta la simbologia umana, le 256 permutazioni di un byte erano insufficienti e sono state ideate codifiche per utilizzare selettivamente più byte.
George,

Risposte:


1855

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

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

Per quegli obiettivi, sinceramente non capisco perché le persone continuino a dirti che hai bisogno delle codifiche. Non devi certo 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;
}

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

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

Ulteriori vantaggi di questo approccio:

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

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

Se hai utilizzato una codifica specifica, tuttavia, ciò avrebbe causato problemi con la codifica / decodifica di caratteri non validi.


248
La cosa brutta di questo è che GetStringe che GetBytesdevono essere eseguiti su un sistema con la stessa endianness per funzionare. Quindi non puoi usarlo per ottenere byte che vuoi trasformare in una stringa altrove. Quindi ho difficoltà a trovare situazioni in cui vorrei usarlo.
CodesInChaos,

72
@CodeInChaos: Come ho detto, il punto è che si desidera utilizzarlo sullo stesso tipo di sistema, con lo stesso set di funzioni. Altrimenti, non dovresti usarlo.
user541686,

193
-1 Garantisco che qualcuno (che non capisce byte vs caratteri) vorrà convertire la propria stringa in una matrice di byte, la cercheranno su Google e leggeranno questa risposta e faranno la cosa sbagliata, perché in quasi tutti casi, la codifica È rilevante.
artbristol,

401
@artbristol: se non possono essere disturbati a leggere la risposta (o le altre risposte ...), allora mi dispiace, quindi non c'è modo migliore per comunicare con loro. In genere scelgo di rispondere all'OP piuttosto che cercare di indovinare cosa potrebbero fare gli altri con la mia risposta: l'OP ha il diritto di sapere, e solo perché qualcuno potrebbe abusare di un coltello non significa che dobbiamo nascondere tutti i coltelli del mondo per noi stessi. Anche se non sei d'accordo, va bene lo stesso.
user541686,

185
Questa risposta è sbagliata su così tanti livelli ma soprattutto a causa della sua declerazione "NON devi preoccuparti della codifica!". I 2 metodi, GetBytes e GetString sono superflui in quanto sono semplicemente re-implementazioni di ciò che già fanno Encoding.Unicode.GetBytes () e Encoding.Unicode.GetString (). L'istruzione "Fintanto che il tuo programma (o altri programmi) non tenta di interpretare i byte" è anche fondamentalmente imperfetta in quanto implicitamente significano che i byte dovrebbero essere interpretati come Unicode.
David,

1108

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 del perché 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 è attrezzato per gestire caratteri speciali.

Internamente, il framework .NET utilizza UTF-16 per rappresentare le stringhe, quindi se si desidera semplicemente ottenere i byte esatti utilizzati da .NET, utilizzare System.Text.Encoding.Unicode.GetBytes (...).

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


14
Ma perché prendere in considerazione la codifica? Perché non riesco semplicemente a ottenere i byte senza dover vedere quale codifica viene utilizzata? Anche se fosse necessario, l'oggetto String stesso non dovrebbe sapere quale codifica viene utilizzata e semplicemente scaricare ciò che è in memoria?
Agnel Kurian,

57
Le stringhe .NET sono sempre codificate come Unicode. Quindi usa System.Text.Encoding.Unicode.GetBytes (); per ottenere il set di byte che .NET utilizzerà per rappresentare i caratteri. Tuttavia, perché lo vorresti? Consiglio UTF-8 soprattutto quando la maggior parte dei personaggi sono nel set latino occidentale.
AnthonyWJones,

8
Inoltre: i byte esatti utilizzati internamente nella stringa non contano se il sistema che li recupera non gestisce quella codifica o la gestisce come codifica errata. Se è tutto all'interno di .Net, perché convertirlo in una matrice di byte. Altrimenti, è meglio essere espliciti con la tua codifica
Joel Coehoorn,

11
@Joel, fai attenzione con System.Text.Encoding.Default in quanto potrebbe essere diverso su ogni macchina che viene eseguita. Ecco perché si consiglia di specificare sempre una codifica, come UTF-8.
Ash,

25
Non hai bisogno delle codifiche a meno che tu (o qualcun altro) non intenda effettivamente interpretare i dati, invece di trattarli come un "blocco di byte" generico. Per cose come la compressione, la crittografia, ecc., Preoccuparsi della codifica non ha senso. Vedi la mia risposta per un modo per farlo senza preoccuparsi della codifica. (Potrei aver dato un -1 per dire che devi preoccuparti delle codifiche quando non lo fai, ma oggi non mi sento particolarmente cattivo.: P)
user541686

285

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


14
Nel caso in cui la risposta accettata venga modificata, ai fini della registrazione, è la risposta di Mehrdad a questa data e ora correnti. Speriamo che il PO riesaminerà questo e accetterà una soluzione migliore.
Thomas Eding,

7
buona in linea di principio, tuttavia, la codifica dovrebbe System.Text.Encoding.Unicodeessere equivalente alla risposta di Mehrdad.
Jodrell,

5
La domanda è stata modificata un milione di volte dalla risposta originale, quindi forse la mia risposta è un po 'datata. Non ho mai avuto intenzione di dare un esatto equivalente alla risposta di Mehrdad, ma dare un modo sensato di farlo. Ma potresti avere ragione. Tuttavia, la frase "ottieni in quali byte è stata memorizzata la stringa" nella domanda originale è molto imprecisa. Memorizzato, dove? In memoria? Sul disco? Se in memoria, System.Text.Encoding.Unicode.GetBytessarebbe probabilmente più preciso.
Erik A. Brandstadmoen,

7
@AMissico, il tuo suggerimento è errato, a meno che tu non sia sicuro che la tua stringa sia compatibile con la codifica predefinita del tuo sistema (stringa contenente solo caratteri ASCII nel tuo set legacy predefinito di sistema). Ma da nessuna parte l'OP lo afferma.
Frédéric,

5
@AMissico Può tuttavia causare al programma risultati diversi su sistemi diversi . Non è mai una buona cosa. Anche se serve per creare un hash o qualcosa del genere (suppongo che questo significhi OP con 'encrypt'), la stessa stringa dovrebbe sempre dare sempre lo stesso hash.
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());

2
Puoi usare la stessa istanza di BinaryFormatter per tutte quelle operazioni
Joel Coehoorn,

3
Molto interessante. Apparentemente lascerà cadere qualsiasi personaggio Unicode surrogato alto. Vedere la documentazione su [BinaryFormatter ]

95

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

Joel ha pubblicato un post su questo:

Il minimo assoluto Ogni sviluppatore di software deve assolutamente conoscere positivamente gli Unicode e i set di caratteri (senza scuse!)


6
"1 carattere può essere rappresentato da 1 o più byte" Concordo. Voglio solo quei byte indipendentemente dalla codifica della stringa. L'unico modo in cui una stringa può essere archiviata in memoria è in byte. Anche i caratteri vengono memorizzati come 1 o più byte. Voglio solo mettere le mani su quei byte.
Agnel Kurian,

16
Non hai bisogno delle codifiche a meno che tu (o qualcun altro) non intenda effettivamente interpretare i dati, invece di trattarli come un "blocco di byte" generico. Per cose come la compressione, la crittografia, ecc., Preoccuparsi della codifica non ha senso. Vedi la mia risposta per un modo per farlo senza preoccuparsi della codifica.
user541686

9
@Mehrdad - Totalmente, ma la domanda originale, come affermato quando ho risposto inizialmente, non ha messo in guardia su cosa OP sarebbe successo con quei byte dopo che li avevano convertiti, e per i futuri ricercatori le informazioni intorno a ciò che sono pertinenti - questo è coperto dalla risposta di Joel abbastanza bene - e mentre affermi nella tua risposta: purché ti attieni al mondo .NET e usi i tuoi metodi per convertirti in / da, sei felice. Non appena esci da questo, la codifica avrà importanza.
Zhaph - Ben Duguid,

Un punto di codice può essere rappresentato da un massimo di 4 byte. (Un'unità di codice UTF-32, una coppia surrogata UTF-16 o 4 byte di UTF-8). I valori per i quali UTF-8 richiederebbero più di 4 byte sono al di fuori dell'intervallo 0x0..0x10FFFF di Unicode. ;-)
DevSolar,

89

Questa è una domanda popolare È importante capire cosa si pone l'autore della domanda e che è diverso da quello che è probabilmente il bisogno più comune. Per scoraggiare l'uso improprio del codice laddove non è necessario, ho risposto prima in un secondo momento.

Esigenza comune

Ogni stringa ha un set di caratteri e una codifica. Quando converti un System.Stringoggetto in un array, System.Bytehai ancora un set di caratteri e una codifica. Per la maggior parte degli utilizzi, sapresti quale set di caratteri e codifica hai bisogno e .NET semplifica la "copia con conversione". Basta scegliere la Encodingclasse appropriata .

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

La conversione potrebbe dover gestire i casi in cui il set di caratteri di destinazione o la codifica non supportano un carattere che si trova nella fonte. Hai alcune scelte: eccezione, sostituzione o salto. La politica di default è 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 perdita!

Nota: per System.Stringil set di caratteri di origine è Unicode.

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

Questo è tutto per la maggior parte degli usi. Se è quello che ti serve, smetti di leggere qui. Vedi il divertente articolo di Joel Spolsky se non capisci cos'è una codifica.

Bisogno specifico

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

Non vuole alcuna conversione.

Dalle specifiche 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 intermedio è accettabile, esiste un collegamento concettuale per questo:

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

Questo non ci fornisce 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! E usa troppo esplicitamente il codice specifico della codifica: il tipo di dati System.Char.

L'unico modo per raggiungere i byte effettivi in ​​cui è memorizzata la stringa è utilizzare un puntatore. L' fixedistruzione consente di prendere l'indirizzo dei valori. Dalle specifiche C #:

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

Per fare ciò, il compilatore scrive il salto del codice 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 dall'endianità della macchina. Ma l'autore della domanda non si preoccupa di questo.


3
@Jan È corretto, ma la lunghezza della stringa indica già il numero di unità di codice (non punti di codice).
Tom Blodget,

1
Grazie per la segnalazione! Da MSDN: "La Lengthproprietà [di String] restituisce il numero di Charoggetti in questa istanza, non il numero di caratteri Unicode." Il tuo codice di esempio è quindi corretto come scritto.
Jan Hettich,

1
@supercat "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." —_ Specifica C # 5._ Sebbene, sì, non c'è nulla che impedisca una stringa Unicode non valida:new String(new []{'\uD800', '\u0030'})
Tom Blodget,

1
@TomBlodget: È interessante notare che, se si prendono istanze di Globalization.SortKey, estrae KeyDatae racchiude i byte risultanti da ciascuno in String[due byte per carattere, prima MSB ], invocare String.CompareOrdinalle stringhe risultanti sarà sostanzialmente più veloce di richiamare SortKey.Comparele istanze di SortKey, o persino chiamando memcmpquelle istanze. Detto questo, mi chiedo perché KeyDatarestituisce un Byte[]anziché un String?
supercat

1
Ahimè, la risposta giusta, ma anni troppo tardi, non avrà mai tanti voti quanti sono stati accettati. A causa di TL, la gente del DR penserà che la risposta accettata oscilli. copia e vota.
Martin Capodici,

46

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

Risponderò alla tua domanda di follow-up: perché devi scegliere una codifica? Perché non puoi ottenerlo dalla stessa classe di stringhe?

La risposta è in due parti.

Prima di tutto, i byte usati internamente dalla classe stringa non contano , e ogni volta che si presume che lo facciano probabilmente stai introducendo un bug.

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

D'altra parte, cosa succede se si stanno inviando questi byte da qualche parte che non è possibile garantire che attireranno i dati da un flusso serializzato .Net? In questo caso devi assolutamente preoccuparti della codifica, perché ovviamente questo sistema esterno se ne frega. Quindi, di nuovo, i byte interni usati dalla stringa non contano: devi scegliere una codifica in modo da poter essere esplicito 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 un po 'di lavoro creando il tuo flusso di byte. Tuttavia, te lo dico, non è solo importante rispetto ad assicurarsi che il tuo output sia compreso dall'altra parte e per garantire che tu debba essere esplicito con la tua codifica. Inoltre, se vuoi davvero abbinare i tuoi byte interni, puoi già semplicemente scegliere la Unicodecodifica e ottenere quel risparmio di prestazioni.

Il che mi porta alla seconda parte ... scegliere la Unicodecodifica sta dicendo a .Net di usare i byte sottostanti. Devi scegliere questa codifica, perché quando esce un nuovo Unicode-Plus, il runtime .Net deve essere libero di usare questo nuovo modello di codifica migliore senza interrompere il tuo programma. Ma, per il momento (e il futuro prevedibile), solo scegliendo la codifica Unicode ti dà quello che vuoi.

È anche importante capire che la stringa deve essere riscritta su wire e ciò comporta almeno una traduzione del 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, pacchetti, informazioni sulla sessione, ecc.


9
Esistono aree in .NET in cui è necessario ottenere array di byte per le stringhe. Molte classi di crittografia .NET contengono metodi come ComputeHash () che accettano array di byte o flusso. Non hai altra alternativa che convertire prima una stringa in un array di byte (scegliendo una codifica) e poi opzionalmente avvolgendola in un flusso. Tuttavia, fino a quando si sceglie una codifica (cioè UTF8) un bastone con essa, non ci sono problemi con questo.
Ash,

44

Giusto per dimostrare che il suono di Mehrdrad risposta opere, il suo approccio può anche persistono i caratteri surrogati spaiati (di cui molti avevano mosse contro la mia risposta, ma di cui tutti sono ugualmente colpevoli di, ad esempio System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; quei metodi di codifica non possono persistere il surrogato alto caratteri d800per esempio, e quelli semplicemente sostituiscono caratteri surrogati alti 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 i caratteri surrogati elevati con valore fffd

Ogni volta che c'è un movimento in questa domanda, sto ancora pensando a un serializzatore (che sia di Microsoft o di componenti di terze parti) che può persistere nelle stringhe anche se contiene caratteri surrogati spaiati; Ogni tanto google questo: serializzazione non surrogata del personaggio surrogato .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 difettose quando si tratta di personaggi surrogati spaiati.

Accidenti, Microsoft avrebbe dovuto usare solo il System.Buffer.BlockCopysuo BinaryFormatter

谢谢!


3
I surrogati non devono apparire in coppia per formare punti di codice validi? In tal caso, posso capire perché i dati sarebbero stati alterati.
dandra

1
Sì, sono anche i miei pensieri, devono apparire in coppia, i personaggi surrogati spaiati si verificano solo se li metti deliberatamente sulla corda e li rendi spaiati. Quello che non so è il motivo per cui altri sviluppatori continuano a sostenere che dovremmo usare invece un approccio consapevole della codifica, poiché hanno ritenuto che l'approccio di serializzazione (la mia risposta , che è stata una risposta accettata per più di 3 anni) non mantenga gli accoppiati personaggio surrogato intatto. Ma si sono dimenticati di verificare che le loro soluzioni consapevoli della codifica non mantengano anche il personaggio surrogato spaiato, l'ironia ツ
Michael Buen,

Se esiste una libreria di serializzazione che utilizza System.Buffer.BlockCopyinternamente, tutti gli argomenti della gente di codifica-advocacy saranno discutibili
Michael Buen,

2
@MichaelBuen Mi sembra che il problema principale sia che sei in grandi lettere in grassetto a dire che qualcosa non importa, piuttosto che dire che non importa nel loro caso. Di conseguenza, stai incoraggiando le persone che guardano la tua risposta a fare errori di programmazione di base che causeranno frustrazioni agli altri in futuro. I surrogati non accoppiati non sono validi in una stringa. Non è un array di caratteri, quindi ha senso che la conversione di una stringa in un altro formato comporterebbe un errore FFFDsu quel carattere. Se si desidera eseguire la manipolazione manuale delle stringhe, utilizzare un carattere [] come raccomandato.
Trisped

2
@dtanders: A System.Stringè una sequenza immutabile di Char; .NET ha sempre permesso a un Stringoggetto di essere costruito da qualsiasi Char[]ed esportare il suo contenuto in un Char[]contenente gli stessi valori, anche se l'originale Char[]contiene surrogati non accoppiati.
supercat

41

Prova questo, molto meno codice:

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

Allora prova questo System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);e piangi! System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Length"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
Funzionerà

9
@ mg30rg: Perché pensi che il tuo esempio sia strano? Sicuramente in una codifica a larghezza variabile non tutti i caratteri hanno la stessa lunghezza di byte. Che cosa c'è che non va?
Vlad

@Vlad Un commento più valido qui, tuttavia, è che come simboli unicode codificati (quindi, come byte), i caratteri che includono i loro propri segni diacritici daranno un risultato diverso rispetto ai segni diacritici suddivisi in simboli modificatori aggiunti al personaggio. Ma iirc ci sono metodi in .net per dividere specificamente quelli fuori, per consentire di ottenere una rappresentazione di byte coerente.
Nyerguds

25

Bene, ho letto tutte le risposte e riguardavano l'uso della codifica o uno sulla serializzazione che elimina surrogati spaiati.

È negativo quando la stringa, ad esempio, proviene da SQL Server, dove è stata creata da un array di byte che archivia, ad esempio, un hash della password. Se ne eliminiamo qualcosa, memorizzerà un hash non valido e se vogliamo memorizzarlo in XML, vogliamo lasciarlo intatto (perché il writer XML rilascia un'eccezione su qualsiasi surrogato spaiato che trova).

Quindi uso la codifica Base64 degli array di byte in questi casi, ma ehi, su Internet c'è solo una soluzione a questo in C #, e contiene un bug ed è solo un modo, quindi ho corretto il bug e riscritto procedura. Ecco a voi, 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);
}

Invece di usare il tuo metodo personalizzato per convertire un array di byte in base64, tutto quello che dovevi fare era usare il convertitore integrato: Convert.ToBase64String (arr);
Makotosan,

@Makotosan grazie, ma l'ho usato Convert.ToBase64String(arr); per le conversioni base64 byte[] (data) <-> string (serialized data to store in XML file). Ma per ottenere l'iniziale byte[] (data)avevo bisogno di fare qualcosa con un Stringcontenuto binario (è il modo in cui MSSQL me lo ha restituito). Quindi le funzioni sopra sono per String (binary data) <-> byte[] (easy accessible binary data).
Gman,

23

Inoltre, spiega perché la codifica dovrebbe essere presa in considerazione. Non posso semplicemente ottenere in quali byte è stata memorizzata la stringa? Perché questa dipendenza dalla codifica? !!!

Perché non esistono "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 solo gestire byte. Pertanto, se si desidera archiviare o trasmettere 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 caratteri logici in byte fisici. La codifica più semplice e conosciuta è ASCII, ed è tutto ciò che serve se si scrive in inglese. Per altre lingue avrai 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 usare le codifiche" è impossibile quanto "scrivere un testo senza usare alcuna lingua".

A proposito, consiglio vivamente a te (e chiunque altro, per questo) di leggere questo piccolo pezzo di saggezza: il minimo assoluto che ogni sviluppatore di software deve assolutamente, positivamente conoscere su Unicode e set di caratteri (senza scuse!)


2
Consentitemi di chiarire: è stata utilizzata una codifica per tradurre "ciao mondo" in byte fisici. Poiché la stringa è memorizzata sul mio computer, sono sicuro che deve essere archiviata in byte. Voglio semplicemente accedere a quei byte per salvarli sul disco o per qualsiasi altro motivo. Non voglio interpretare questi byte. Dal momento che non voglio interpretare questi byte, la necessità di una codifica a questo punto è fuori luogo quanto richiedere una linea telefonica per chiamare printf.
Agnel Kurian,

3
Ma ancora una volta, non esiste un concetto di traduzione da testo a byte fisico a meno che non si usi una codifica. Certo, il compilatore memorizza le stringhe in qualche modo in memoria, ma sta semplicemente usando una codifica interna, che tu (o chiunque tranne lo sviluppatore del compilatore) non conosci. Quindi, qualunque cosa tu faccia, hai bisogno di una codifica per ottenere byte fisici da una stringa.
Konamiman,

@Agnel Kurian: È certamente vero che una stringa ha un sacco di byte da qualche parte che ne memorizzano il contenuto (UTF-16 inavvertitamente). Ma c'è una buona ragione per impedirti di accedervi: le stringhe sono immutabili e se tu potessi ottenere l'array byte [] interno, potresti anche modificarlo. Questo rompe l'immutabilità, che è vitale perché più stringhe possono condividere gli stessi dati. L'uso di una codifica UTF-16 per ottenere la stringa probabilmente copierà semplicemente i dati.
ollb

2
@Gnafoo, Una copia dei byte farà.
Agnel Kurian,

22

C # per convertire a stringin un bytearray:

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

17
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}

Ma perché prendere in considerazione la codifica? Perché non riesco semplicemente a ottenere i byte senza dover vedere quale codifica viene utilizzata? Anche se fosse necessario, l'oggetto String stesso non dovrebbe sapere quale codifica viene utilizzata e semplicemente scaricare ciò che è in memoria?
Agnel Kurian,

5
Questo non funziona sempre. Alcuni personaggi speciali possono perdersi usando un metodo del genere che ho trovato nel modo più difficile.
JB King,

17

È possibile utilizzare il seguente codice per la conversione tra stringa e array di 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);

VUPquesto risolve il mio problema (byte [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd

16

Con l'avvento del Span<T>rilascio con C # 7.2, la tecnica canonica per acquisire la rappresentazione di memoria sottostante di una stringa in un array di byte gestiti è:

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

La riconversione dovrebbe essere un non-avviamento perché ciò significa che in realtà 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 NonPortableCaste DangerousGetPinnableReferencedovrebbero favorire l'argomento secondo cui probabilmente non dovresti farlo.

Si noti che per lavorare è Span<T>necessario installare il pacchetto NuGet System.Memory .

Indipendentemente da ciò, l' attuale domanda originale e i commenti di follow-up implicano che la memoria sottostante non viene "interpretata" (che presumo significhi che non viene modificata o letta oltre la necessità di scriverla così com'è), indicando che alcune implementazioni della Streamclasse dovrebbe essere usato invece di ragionare sui dati come stringhe.


13

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

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

Si noti che la risposta Unicode è di 14 byte in entrambi i casi, mentre la risposta UTF-8 è di soli 9 byte per il primo e solo 7 per il secondo.

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


10

Il problema chiave è che un glifo in una stringa richiede 32 bit (16 bit per un codice carattere) ma un byte ha solo 8 bit da risparmiare. Una mappatura individuale non esiste se non ti limiti alle stringhe che contengono solo caratteri ASCII. System.Text.Encoding ha molti modi per mappare una stringa su 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 con perdite.


3
UTF-8 è compatto solo se la maggior parte dei personaggi si trova nel set di caratteri inglese (ASCII). Se avessi una lunga serie di caratteri cinesi, UTF-16 sarebbe una codifica più compatta rispetto a UTF-8 per quella stringa. Questo perché UTF-8 utilizza un byte per codificare ASCII e 3 (o forse 4) in caso contrario.
Joel Mueller,

7
Vero. Ma come non conoscere la codifica se si ha familiarità con la gestione del testo cinese?
Hans Passant,

9

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

OP chiede specificamente di NON specificare una codifica ... "senza specificare manualmente una codifica specifica"
Ferdz,

8

Modo più veloce

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

EDIT come ha commentato Makotosan, questo è ora il modo migliore:

Encoding.UTF8.GetBytes(text)

8
ASCIIEncoding ..... non è necessario. È preferibile semplicemente usare Encoding.UTF8.GetBytes (testo).
Makotosan,

8

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

Una stringa 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 guardando l'origine crea un char[]e copia direttamente la memoria su di esso. Quindi copia i dati in un array di byte anch'esso allocato. Quindi, sotto il cofano, copia due volte i byte sottostanti e alloca un array di caratteri che non viene utilizzato dopo la chiamata.

La risposta di Tom Blodget

La risposta di Tom Blodget è del 20-30% più veloce di Mehrdad poiché salta la fase intermedia di allocazione di un array di caratteri e copia dei byte su di esso, ma richiede la compilazione con l' /unsafeopzione. Se non vuoi assolutamente usare la codifica, penso che questa sia la strada da percorrere. Se inserisci l'accesso di crittografia all'interno del fixedblocco, non è nemmeno necessario allocare un array di byte separato e copiarvi i byte.

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

Perché questo è il modo corretto di farlo. stringè un'astrazione.

L'uso di una codifica potrebbe creare problemi se hai "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 lo usi System.Text.Encoding.Unicode, il tuo codice sarà più resiliente. Non devi preoccuparti dell'endianità del sistema su cui verrà eseguito il tuo codice. Non devi preoccuparti se la prossima versione di CLR utilizzerà una codifica di caratteri interna diversa.

Penso che la domanda non sia perché vuoi preoccuparti della codifica, ma perché vuoi ignorarla e usare qualcos'altro. La codifica intende rappresentare l'astrazione di una stringa in una sequenza di byte. System.Text.Encoding.Unicodeti darà un po 'di codifica dell'ordine dei byte endian ed eseguirà lo stesso su tutti i sistemi, ora e in futuro.


In realtà una stringa in C # NON è limitata al solo UTF-16. Ciò che è vero è che contiene un vettore di unità di codice a 16 bit, ma queste unità di codice a 16 bit non sono limitate a UTF-16 valido. Ma poiché sono a 16 bit, è necessaria una codifica (ordine byte) per convertirli in 8 bit. Una stringa può quindi archiviare dati non Unicode, incluso il codice binario (ad es. Un'immagine bitmap). Viene interpretato come UTF-16 solo in I / O e formattatori di testo che eseguono tale interpretazione.
verdy_p,

Pertanto, in una stringa C #, è possibile memorizzare in modo sicuro un'unità di codice come 0xFFFF o 0xFFFE, anche se non sono caratteri in UTF-16, e è possibile memorizzare uno 0xD800 isolato non seguito da un'unità di codice in 0xDC00..0xDFFF surrogati spaiati non validi in UTF-16). La stessa osservazione vale per le stringhe in Javascript / ECMAscript e Java.
verdy_p

Quando si utilizza "GetBytes", ovviamente non si specifica una codifica, ma si assume un ordine di byte per ottenere i due byte in una specifica per ogni unità di codice memorizzata localmente nella stringa. Quando si crea una nuova stringa da byte, è necessario anche un convertitore, non necessariamente da UTF-8 a UTF-16, è possibile inserire lo 0 aggiuntivo nel byte alto o comprimere due byte (nel primo ordine MSB o nel primo ordine LSB) in la stessa unità di codice a 16 bit. Le stringhe sono quindi in forma compatta per array di numeri interi a 16 bit. La relazione con i "caratteri" è un altro problema, in C # non sono tipi reali in quanto sono ancora rappresentati come stringhe
verdy_p

7

L'approccio più vicino alla domanda del PO è quello 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 dati interessante. Sappiamo già che lo fa, perché consente di conservare surrogati non accoppiati. Potrebbe memorizzare la lunghezza. Potrebbe tenere un puntatore a ciascuno dei surrogati "accoppiati" consentendo un conteggio rapido. Ecc. Tutti questi byte extra non fanno parte dei dati dei caratteri.

Quello che vuoi sono i byte di ogni personaggio in un array. Ed è qui che entra in gioco la "codifica". Per impostazione predefinita otterrai UTF-16LE. Se non ti interessano i byte stessi ad eccezione del round trip, puoi scegliere qualsiasi codifica incluso il 'default', e riconvertirla in seguito (assumendo gli stessi parametri come quello che era la codifica predefinita, punti di codice, correzioni di bug , cose consentite come surrogati spaiati, ecc.

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

"Why is there a dependency on character encodings?"

La 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 - è qui che la domanda è stata posta in modo ingenuo. Volevi i byte di stringa in un array contiguo che rappresenta la stringa e non tutti gli altri dati binari che un oggetto stringa può contenere.

Ciò significa che la memorizzazione di una stringa è irrilevante. Volete una stringa "Codificata" in byte in una matrice di byte.

Mi piace la risposta di Tom Bloget perché ti ha portato verso la direzione dei "byte dell'oggetto stringa". Tuttavia, dipende dall'implementazione e, poiché sta sbirciando all'interno, potrebbe essere difficile ricostituire una copia della stringa.

La risposta di Mehrdad è sbagliata perché fuorviante a livello concettuale. Hai ancora un elenco di byte, codificato. La sua particolare soluzione consente di preservare surrogati spaiati - questo dipende dall'implementazione. La sua soluzione particolare non produrrebbe i byte GetBytesdella stringa in modo accurato se 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 della matrice di caratteri creata dalla stringa. Indipendentemente dalla codifica, il tipo di dati char in c # è una dimensione fissa. Ciò consente di produrre un array di byte di lunghezza costante e di riprodurre l'array di caratteri in base alle dimensioni dell'array di byte. Quindi, se la codifica fosse UTF-8, ma ogni carattere era di 6 byte per contenere il valore utf8 più grande, funzionerebbe comunque. Quindi davvero - 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, non importa quale sia tale rappresentazione, che è tecnicamente la risposta al PO. Quindi, se hai intenzione di convertire comunque ... Perché non "codificare"?


Questi caratteri non sono supportati da UTF-8 o UTF-16 o anche UTF-32 per l'esempio: 񩱠& (Char) 55906& (Char) 55655. Quindi potresti avere torto e la risposta di Mehrdad è una conversione sicura senza considerare il tipo di codifiche utilizzate.
Mojtaba Rezaeian,

Raymon, i personaggi sono già rappresentati da un valore unicode - e tutti i valori unicode possono essere rappresentati da tutti gli utf. C'è una spiegazione più lunga di cosa stai parlando? In quale codifica dei caratteri esistono questi due valori (o 3 ..)?
Gerard ONeill,

Sono caratteri non validi che non sono supportati da nessun intervallo di codifica. Ciò non significa che siano inutili al 100%. Un codice che converte qualsiasi tipo di stringa nel suo equivalente di array di byte indipendentemente dalle codifiche non è affatto una soluzione sbagliata e ha i suoi usi nelle occasioni desiderate.
Mojtaba Rezaeian,

1
Ok, allora penso che non stai capendo il problema. Sappiamo che è un array conforme unicode - infatti, poiché è .net, sappiamo che è UTF-16. Quindi quei personaggi non esisteranno lì. Inoltre non hai letto completamente il mio commento sul cambiamento delle rappresentazioni interne. Una stringa è un oggetto, non un array di byte codificato. Quindi non sarò d'accordo con la tua ultima affermazione. Vuoi che il codice converta tutte le stringhe unicode in qualsiasi codifica UTF. Questo fa quello che vuoi, correttamente.
Gerard ONeill,

Gli oggetti sono una sequenza di dati originariamente una sequenza di bit che descrivono un oggetto nel suo stato attuale. Pertanto, tutti i dati nei linguaggi di programmazione sono convertibili in array di byte (ogni byte definisce 8 bit) poiché potrebbe essere necessario mantenere un certo stato di qualsiasi oggetto in memoria. È possibile salvare e conservare una sequenza di byte nel file o nella memoria e lanciarlo come numero intero, bigint, immagine, stringa Ascii, stringa UTF-8, stringa crittografata o tipo di dati definito dopo averlo letto dal disco. Quindi non si può dire che gli oggetti siano qualcosa di diverso dalla sequenza di byte.
Mojtaba Rezaeian,

6

È possibile utilizzare il seguente codice per convertire stringa byte arrayin a .NET

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

3

Se vuoi davvero una copia dei byte sottostanti di una stringa, puoi usare una funzione come quella che segue. Tuttavia, non dovresti continuare a 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 darà una copia dei byte sottostanti la tua stringa, abbastanza rapidamente. Otterrai quei byte in qualunque modo essi stiano codificando sul tuo sistema. Questa codifica è quasi certamente UTF-16LE ma è un dettaglio di implementazione di cui non dovresti preoccuparti.

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

System.Text.Encoding.Unicode.GetBytes()

Con ogni probabilità questo darà lo stesso risultato, è più facile da digitare e i byte andranno sempre di andata e ritorno con una chiamata a

System.Text.Encoding.Unicode.GetString()

3

Ecco la mia implementazione non sicura di Stringalla Byte[]conversione:

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 quello di una persona accettata, anche se non così elegante come è. Ecco i miei benchmark Cronometro 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, è necessario selezionare "Consenti codice non sicuro" nelle proprietà di compilazione del 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
    }
}

Il valore di RuntimeHelpers.OffsetToStringDataun multiplo di 8 è nelle versioni Itanium di .NET? Perché altrimenti ciò fallirà a causa delle letture non allineate.
Jon Hanna,

non sarebbe più semplice invocare memcpy? stackoverflow.com/a/27124232/659190
Jodrell

2

Basta usare questo:

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

2
... e perde tutti i personaggi con un salto superiore a 127. Nella mia lingua madre è perfettamente valido scrivere "Árvíztűrő tükörfúrógép.". System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();restituirà "Árvizturo tukörfurogép."informazioni perse che non possono essere recuperate. (E non ho ancora menzionato le lingue asiatiche in cui perderai tutti i caratteri.)
mg30rg

2

La stringa può essere convertita in array di byte in diversi modi, a causa del seguente fatto: .NET supporta Unicode e Unicode standardizza diverse codifiche di differenza chiamate UTF. Hanno diverse lunghezze di rappresentazione dei byte ma sono equivalenti in quel senso che quando una stringa è codificata, può essere codificata nuovamente sulla 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 (saranno valide solo se un sottoinsieme limitato di punti di codice Unicode viene utilizzato in una stringa effettiva, come ASCII). Internamente, .NET supporta UTF-16, ma per la rappresentazione dello stream viene solitamente utilizzato UTF-8. È anche uno standard di fatto per Internet.

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

Rif questo link.

Per la serializzazione su una matrice di byte mediante System.Text.Encoding.GetBytes. Per l'operazione inversa utilizzareSystem.Text.Encoding.GetChars . Questa funzione restituisce una matrice di caratteri, quindi per ottenere una stringa, usa 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

Dipende da cosa vuoi i byte PER

Questo perché, come ha giustamente affermato Tyler , "le stringhe non sono dati puri. Hanno anche informazioni ". In questo caso, l'informazione è una codifica che è stata assunta al momento della creazione della stringa.

Supponendo che tu abbia dati binari (piuttosto che 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 sul caso d'uso.

La memorizzazione di dati binari nelle stringhe è probabilmente l'approccio sbagliato a causa della presunta codifica menzionata sopra! Qualunque programma o libreria abbia archiviato quei dati binari in un string(anziché in un byte[]array che sarebbe stato più appropriato) ha già perso la battaglia prima che iniziasse. Se ti stanno inviando i byte in una richiesta / risposta REST o in 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 essere così com'è, basta scegliere una codifica (preferibilmente una che inizia con UTF), utilizzare la System.Text.Encoding.???.GetBytes()funzione corrispondente e dire a chi si danno i byte a quale codifica hai scelto.


2

Quando ti viene chiesto cosa intendi fare con i byte, hai risposto :

Ho intenzione di crittografarlo. Posso crittografarlo senza convertirlo, ma mi piacerebbe comunque sapere perché la codifica viene riprodotta qui. Dammi solo i byte è quello che dico.

Indipendentemente dal fatto che tu abbia intenzione di inviare questi dati crittografati sulla rete, caricarli di nuovo in memoria in un secondo momento o trasferirli a vapore in un altro processo, hai chiaramente intenzione di decrittografarli a un certo punto. In tal caso, la risposta è che stai definendo un protocollo di comunicazione. Un protocollo di comunicazione non deve essere definito in termini di dettagli di implementazione del linguaggio di programmazione e del runtime associato. Ci sono diverse ragioni per questo:

  • Potrebbe essere necessario comunicare con un processo implementato in un'altra lingua o runtime. (Ciò potrebbe includere un server in esecuzione su un altro computer o l'invio della stringa a un client browser JavaScript, ad esempio.)
  • Il programma potrebbe essere reimplementato in un'altra lingua o runtime in futuro.
  • L'implementazione di .NET potrebbe cambiare la rappresentazione interna delle stringhe. Potresti pensare che questo sembri inverosimile, ma in realtà è successo in Java 9 per ridurre l'utilizzo della memoria. Non c'è motivo per cui .NET non possa seguirne l'esempio. Skeet suggerisce che oggi UTF-16 probabilmente non è ottimale per far emergere le emoji e altri blocchi di Unicode che richiedono oltre 2 byte per rappresentare, aumentando la probabilità che la rappresentazione interna possa cambiare in futuro.

Per comunicare (con un processo completamente diverso o con lo stesso programma in futuro), è necessario definire il protocollo in modo rigoroso per ridurre al minimo la difficoltà di lavorare con esso o di creare accidentalmente bug. A seconda della rappresentazione interna di .NET, la definizione non è rigorosa, chiara o addirittura garantita. Una codifica standard è una definizione rigorosa che non ti deluderà in futuro.

In altre parole, non puoi soddisfare i tuoi requisiti di coerenza senza specificare una codifica.

Puoi certamente scegliere di utilizzare UTF-16 direttamente se ritieni che il tuo processo funzioni in modo significativamente migliore poiché .NET lo utilizza internamente o per qualsiasi altro motivo, ma devi scegliere quella codifica in modo esplicito ed eseguire quelle conversioni in modo esplicito nel tuo codice anziché dipendere sull'implementazione interna di .NET.

Quindi scegli una codifica e usala:

using System.Text;

// ...

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

Come puoi vedere, in realtà è anche meno codice per usare solo gli oggetti di codifica incorporati che per implementare i tuoi metodi di lettura / scrittura.


1

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 a usare quello inferiore più spesso di quello superiore, non li ho confrontati per la velocità.


4
E i personaggi multibyte?
Agnel Kurian,

c.ToByte () è privato: S
Khodor,

@AgnelKurian Msdn dice "Questo metodo restituisce un valore di byte senza segno che rappresenta il codice numerico dell'oggetto Char passato ad esso. In .NET Framework, un oggetto Char ha un valore di 16 bit. Ciò significa che il metodo è adatto per la restituzione i codici numerici dei caratteri nell'intervallo di caratteri ASCII o nei controlli Unicode C0 e Basic Latin, e C1 Controls e Latin-1 Supplement, da U + 0000 a 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.