Conversione di stringhe in array di byte in C #


670

Sto convertendo qualcosa da VB in C #. Problemi con la sintassi di questa affermazione:

if ((searchResult.Properties["user"].Count > 0))
{
    profile.User = System.Text.Encoding.UTF8.GetString(searchResult.Properties["user"][0]);
}

Vedo quindi i seguenti errori:

Argomento 1: impossibile convertire da 'oggetto' a 'byte []'

La migliore corrispondenza del metodo sovraccarico per "System.Text.Encoding.GetString (byte [])" ha alcuni argomenti non validi

Ho provato a correggere il codice in base a questo post, ma ancora non ci sono riuscito

string User = Encoding.UTF8.GetString("user", 0);

Eventuali suggerimenti?


1
Di che tipo è searchResult.Properties["user"][0]? Prova a lanciarlo per byte[]primo
mshsayem il

mshsayem è andato dove stavo andando. Ti manca un cast per un (byte[])su searchResult?
Harrison,

2
Devi scoprire che tipo Properties["user"][0]è. Se sei sicuro che sia un array di byte, puoi eseguire il cast in questo modoprofile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
keyboardP

1
Si scopre che non c'era bisogno di tutto quel clamore. Dopotutto, il nome utente potrebbe essere recuperato senza codifica.
nouptime

3
Perché non selezioni la risposta vera?
Ali,

Risposte:


1189

Se disponi già di un array di byte, dovrai sapere quale tipo di codifica è stato utilizzato per trasformarlo in tale array di byte.

Ad esempio, se l'array di byte è stato creato in questo modo:

byte[] bytes = Encoding.ASCII.GetBytes(someString);

Dovrai trasformarlo in una stringa come questa:

string someString = Encoding.ASCII.GetString(bytes);

Se riesci a trovare nel codice che hai ereditato, la codifica utilizzata per creare l'array di byte dovrebbe essere impostata.


3
Timothy, ho controllato il codice VB e non riesco a trovare un array di byte come hai menzionato.
nouptime

Nel tuo risultato di ricerca, qual è il tipo di proprietà Properties?
Timothy Randall,

Tutto quello che posso vedere è che ci sono un numero di elementi collegati a Proprietà come una stringa. Non sono sicuro che sia quello che mi stavi chiedendo però.
nouptime

16
@AndiAR prova Encoding.UTF8.GetBytes (somestring)
OzBob il

1
Per la mia situazione ho scoperto che Encoding.Unicode.GetBytes ha funzionato (ma ASCII no)
Jeff

106

Prima di tutto, aggiungi lo System.Textspazio dei nomi

using System.Text;

Quindi utilizzare questo codice

string input = "some text"; 
byte[] array = Encoding.ASCII.GetBytes(input);

Spero di risolverlo!


42

Inoltre è possibile utilizzare un metodo di estensione per aggiungere un metodo al stringtipo come di seguito:

static class Helper
{
   public static byte[] ToByteArray(this string str)
   {
      return System.Text.Encoding.ASCII.GetBytes(str);
   }
}

E usalo come di seguito:

string foo = "bla bla";
byte[] result = foo.ToByteArray();

12
Rinominerei quel metodo per includere il fatto che sta usando la codifica ASCII. Qualcosa del genere ToASCIIByteArray. Odio quando scopro che alcune librerie che sto usando usano ASCII e presumo che stia usando UTF-8 o qualcosa di più moderno.
T Blank

30
var result = System.Text.Encoding.Unicode.GetBytes(text);

3
Questa dovrebbe essere la risposta accettata, come le altre risposte suggeriscono ASCII, ma la codifica è Unicode (che è UTF16) o UTF8.
Abel,

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

Questo non riuscirà per i caratteri che rientrano nell'intervallo di coppie surrogate. GetBytes avrà un array di byte che manca un carattere normale per coppia di surrogati alla fine. Alla fine GetString avrà caratteri vuoti. L'unico modo in cui funzionerebbe è se l'impostazione predefinita di Microsoft fosse UTF32 o se i caratteri nell'intervallo di coppie surrogate non fossero consentiti. O c'è qualcosa che non vedo? Il modo corretto è "codificare" la stringa in byte.
Gerard ONeill,

Corretto, per una gamma più ampia puoi usare qualcosa di simile alla soluzione di #Timothy Randall: using System; utilizzando System.Text; namespace Esempio {programma di classe pubblica {void statico pubblico Main (string [] args) {string s1 = "Hello World"; string s2 = "שלום עולם"; string s3 = "你好 , 世界!"; Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (S1))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s2))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (S3))); }}}
Eran Yogev,

17

Perché Encoding.Default non dovrebbe essere usato ...

La risposta di @ Randall utilizza Encoding.Default, tuttavia Microsoft genera un avvertimento :

Computer diversi possono utilizzare codifiche diverse come impostazione predefinita e la codifica predefinita può cambiare su un singolo computer. Se si utilizza la codifica predefinita per codificare e decodificare i dati trasmessi in streaming tra computer o recuperati in momenti diversi sullo stesso computer, è possibile che i dati vengano tradotti in modo errato. Inoltre, la codifica restituita dalla proprietà Default utilizza il fallback più adatto per mappare i caratteri non supportati ai caratteri supportati dalla tabella codici. Per questi motivi, si consiglia di non utilizzare la codifica predefinita. Per garantire che i byte codificati siano decodificati correttamente, è necessario utilizzare una codifica Unicode, come UTF8Encoding o UnicodeEncoding. È inoltre possibile utilizzare un protocollo di livello superiore per garantire che lo stesso formato venga utilizzato per la codifica e la decodifica.

Per verificare quale sia la codifica predefinita, utilizzare Encoding.Default.WindowsCodePage(1250 nel mio caso - e purtroppo non esiste una classe predefinita di codifica CP1250, ma l'oggetto può essere recuperato come Encoding.GetEncoding(1250)).

Encoding.ASCII è 7 bit, quindi non funziona neanche, nel mio caso:

byte[] pass = Encoding.ASCII.GetBytes("šarže");
Console.WriteLine(Encoding.ASCII.GetString(pass)); // ?ar?e

... e perché usare invece la codifica UTF-8 ...

La codifica predefinita è fuorviante: .NET utilizza UTF-8 ovunque come il vero valore predefinito (le codifiche a 8 bit sono diventate obsolete alla fine del 20. secolo, controlla cioè Console.OutputEncoding.EncodingName*), quindi ogni costante definita nel codice è codificata UTF-8 per impostazione predefinita, quindi questo dovrebbe essere usato a meno che l'origine dati non abbia una codifica diversa.

* Questo è UTF-8 nel mio caso che è una bugia diretta: chcpdalla console di Windows (cmd) restituisce 852 - e questo non dovrebbe essere cambiato, perché i comandi di sistema localizzati (come ping) hanno questa codepage codificata

Seguendo le raccomandazioni di Microsoft:

var utf8 = new UTF8Encoding();
byte[] pass = utf8.GetBytes("šarže");
Console.WriteLine(utf8.GetString(pass)); // šarže

Encoding.UTF8 raccomandato da altri è un'istanza di codifica UTF-8 e può essere utilizzato anche direttamente o come

var utf8 = Encoding.UTF8 as UTF8Encoding;

... ma non è sempre usato

La codifica per array di byte dovrebbe "funzionare" in Unicode nei paesi occidentali, ma non appena si sposta il programma in alcune regioni meno supportate (come qui nell'Europa orientale), è un vero casino: nella Repubblica Ceca le impostazioni predefinite di Windows utilizzano (nel 2020!) MS non standard 852 (aka Latin-2) per console, 1250 come OEM Windows, UTF-8 (65001) come .NET (e altri) nuovo predefinito e dovremmo tenere presente che alcuni 8bit UE occidentali i dati sono ancora nel 1252, mentre il vecchio standard occidentale a 8 bit per l'Europa orientale era ISO-8859-2 (aka Latin-2, ma NON lo stesso Latin-2 dell'852). L'uso di ASCII significa testo pieno di tofu e '?' Qui. Quindi, fino alla metà del 21 ° secolo, impostare UTF-8 in modo esplicito .


12

Costruendo la risposta di Ali , consiglierei un metodo di estensione che ti consente di trasferire facoltativamente la codifica che desideri utilizzare:

using System.Text;
public static class StringExtensions
{
    /// <summary>
    /// Creates a byte array from the string, using the 
    /// System.Text.Encoding.Default encoding unless another is specified.
    /// </summary>
    public static byte[] ToByteArray(this string str, Encoding encoding = Encoding.Default)
    {
        return encoding.GetBytes(str);
    }
}

E usalo come di seguito:

string foo = "bla bla";

// default encoding
byte[] default = foo.ToByteArray();

// custom encoding
byte[] unicode = foo.ToByteArray(Encoding.Unicode);

2
Si noti che l'utilizzo dei Encoding encoding = Encoding.Defaultrisultati comporta un errore di compilazione:CS1736 Default parameter value for 'encoding' must be a compile-time constant
Douglas Gaskell,

11

L'approccio seguente funzionerà solo se i caratteri sono 1 byte. (Unicode predefinito non funzionerà poiché è di 2 byte)

public static byte[] ToByteArray(string value)
{            
    char[] charArr = value.ToCharArray();
    byte[] bytes = new byte[charArr.Length];
    for (int i = 0; i < charArr.Length; i++)
    {
        byte current = Convert.ToByte(charArr[i]);
        bytes[i] = current;
    }

    return bytes;
}

Mantenerlo semplice


chare stringsono UTF-16 per definizione.
Tom Blodget,

Sì, il valore predefinito è UTF-16. Non sto facendo ipotesi sulla codifica della stringa di input.
Mandar Sudame,

Non c'è testo ma testo codificato. Il tuo input è di tipo stringed è quindi UTF-16. UTF-16 non è l'impostazione predefinita; non c'è scelta al riguardo. Quindi dividi in char[]unità di codice UTF-16. Quindi chiami Convert.ToByte (Char) , che succede per convertire da U + 0000 a U + 00FF in ISO-8859-1 e manipola qualsiasi altro codice.
Tom Blodget,

Ha senso. Grazie per il chiarimento. Aggiornamento della mia risposta.
Mandar Sudame,

1
Penso che ti manchino ancora alcuni punti essenziali. Concentrati charsull'essere 16 bit e Convert.ToByte()buttarne via metà.
Tom Blodget

10

Usa questo

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

6

Un perfezionamento della modifica di JustinStolle (l'uso di BlockCopy da parte di Eran Yogev).

La soluzione proposta è davvero più rapida rispetto all'utilizzo della codifica. Il problema è che non funziona per la codifica di array di byte di lunghezza irregolare. Come indicato, genera un'eccezione non vincolata. L'aumento della lunghezza di 1 lascia un byte finale durante la decodifica dalla stringa.

Per me, il bisogno è venuto quando volevo codificare da DataTablea JSON. Stavo cercando un modo per codificare i campi binari in stringhe e decodificare da stringa a indietro byte[].

Ho quindi creato due classi: una che avvolge la soluzione precedente (quando si codifica dalle stringhe va bene, perché le lunghezze sono sempre pari) e un'altra che gestisce byte[] codifica.

Ho risolto il problema della lunghezza irregolare aggiungendo un singolo carattere che mi dice se la lunghezza originale dell'array binario era dispari ('1') o pari ('0')

Come segue:

public static class StringEncoder
{
    static byte[] EncodeToBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
    static string DecodeToString(byte[] bytes)
    {
        char[] chars = new char[bytes.Length / sizeof(char)];
        System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
        return new string(chars);
    }
}

public static class BytesEncoder
{
    public static string EncodeToString(byte[] bytes)
    {
        bool even = (bytes.Length % 2 == 0);
        char[] chars = new char[1 + bytes.Length / sizeof(char) + (even ? 0 : 1)];
        chars[0] = (even ? '0' : '1');
        System.Buffer.BlockCopy(bytes, 0, chars, 2, bytes.Length);

        return new string(chars);
    }
    public static byte[] DecodeToBytes(string str)
    {
        bool even = str[0] == '0';
        byte[] bytes = new byte[(str.Length - 1) * sizeof(char) + (even ? 0 : -1)];
        char[] chars = str.ToCharArray();
        System.Buffer.BlockCopy(chars, 2, bytes, 0, bytes.Length);

        return bytes;
    }
}

4

A questa domanda è stata data una risposta sufficiente molte volte, ma con C # 7.2 e l'introduzione del tipo Span, c'è un modo più veloce per farlo in un codice non sicuro:

public static class StringSupport
{
    private static readonly int _charSize = sizeof(char);

    public static unsafe byte[] GetBytes(string str)
    {
        if (str == null) throw new ArgumentNullException(nameof(str));
        if (str.Length == 0) return new byte[0];

        fixed (char* p = str)
        {
            return new Span<byte>(p, str.Length * _charSize).ToArray();
        }
    }

    public static unsafe string GetString(byte[] bytes)
    {
        if (bytes == null) throw new ArgumentNullException(nameof(bytes));
        if (bytes.Length % _charSize != 0) throw new ArgumentException($"Invalid {nameof(bytes)} length");
        if (bytes.Length == 0) return string.Empty;

        fixed (byte* p = bytes)
        {
            return new string(new Span<char>(p, bytes.Length / _charSize));
        }
    }
}

Tieni presente che i byte rappresentano una stringa codificata UTF-16 (chiamata "Unicode" nella terra C #).

Alcuni benchmark rapidi mostrano che i metodi di cui sopra sono circa 5 volte più veloci delle loro implementazioni Encoding.Unicode.GetBytes (...) / GetString (...) per stringhe di medie dimensioni (30-50 caratteri) e persino più veloci per stringhe più grandi. Questi metodi sembrano anche essere più veloci dell'uso dei puntatori con Marshal.Copy (..) o Buffer.MemoryCopy (...).


4

Se il risultato di "searchResult.Properties [" user "] [0]" è una stringa:

if ( ( searchResult.Properties [ "user" ].Count > 0 ) ) {

   profile.User = System.Text.Encoding.UTF8.GetString ( searchResult.Properties [ "user" ] [ 0 ].ToCharArray ().Select ( character => ( byte ) character ).ToArray () );

}

Il punto chiave è che la conversione di una stringa in un byte [] può essere fatta usando LINQ:

.ToCharArray ().Select ( character => ( byte ) character ).ToArray () )

E l'inverso:

.Select ( character => ( char ) character ).ToArray () )

3

Qualcuno vede qualche motivo per non farlo?

mystring.Select(Convert.ToByte).ToArray()

10
Convert.ToByte(char)non funziona come pensi che sarebbe. Il carattere '2'viene convertito nel byte 2, non nel byte che rappresenta il carattere '2'. Usa mystring.Select(x => (byte)x).ToArray()invece.
Jack,


2

È possibile utilizzare l' API MemoryMarshal per eseguire conversioni molto veloci ed efficienti. Stringverrà implicitamente cast ReadOnlySpan<byte>, come MemoryMarshal.Castaccetta uno Span<byte>o ReadOnlySpan<byte>come parametro di input.

public static class StringExtensions
{
    public static byte[] ToByteArray(this string s) => s.ToByteSpan().ToArray(); //  heap allocation, use only when you cannot operate on spans
    public static ReadOnlySpan<byte> ToByteSpan(this string s) => MemoryMarshal.Cast<char, byte>(s);
}

Il seguente benchmark mostra la differenza:

Input: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"

|                       Method |       Mean |     Error |    StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------------------------- |-----------:|----------:|----------:|-------:|------:|------:|----------:|
| UsingEncodingUnicodeGetBytes | 160.042 ns | 3.2864 ns | 6.4099 ns | 0.0780 |     - |     - |     328 B |
| UsingMemoryMarshalAndToArray |  31.977 ns | 0.7177 ns | 1.5753 ns | 0.0781 |     - |     - |     328 B |
|           UsingMemoryMarshal |   1.027 ns | 0.0565 ns | 0.1630 ns |      - |     - |     - |         - |

0

Questo lavoro per me, dopo che ho potuto convertire mettere la mia foto in un campo bytea nel mio database.

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[]))
{
    return s.ToArray();
}
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.