web-dev-qa-db-fra.com

Comment obtenir une représentation cohérente en octets des chaînes en C # sans spécifier manuellement un codage?

Comment convertir une string en byte[] en .NET (C #) sans spécifier manuellement un codage spécifique?

Je vais chiffrer la chaîne. Je peux le chiffrer sans convertir, mais j'aimerais quand même savoir pourquoi le codage vient jouer ici.

Aussi, pourquoi le codage devrait-il être pris en compte? Je ne peux pas simplement obtenir les octets dans lesquels la chaîne a été stockée? Pourquoi existe-t-il une dépendance aux codages de caractères?

2088
Agnel Kurian

Contrairement aux réponses fournies ici, vous n'avez PAS à vous soucier de l'encodage si les octets n'ont pas besoin d'être interprétés !

Comme vous l'avez mentionné, votre objectif est simplement "d'obtenir les octets dans lesquels la chaîne a été stockée" .
(Et, bien sûr, pour pouvoir reconstruire la chaîne à partir des octets.)

Pour ces objectifs, honnêtement, je ne comprends pas pourquoi les gens vous répètent sans cesse que vous avez besoin des encodages. Vous n'avez certainement PAS besoin de vous préoccuper des encodages pour cela.

Faites juste ceci à la place:

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

Tant que votre programme (ou d'autres programmes) n'essaye pas d'interpréter les octets d'une manière ou d'une autre, ce que vous n'avez évidemment pas mentionné que vous avez l'intention de faire, alors il y a rien faux avec cette approche! S'inquiéter des encodages ne fait que rendre votre vie plus compliquée sans raison réelle.

Avantage supplémentaire à cette approche:

Peu importe si la chaîne contient des caractères non valides, car vous pouvez quand même obtenir les données et reconstruire la chaîne d'origine!

Il sera encodé et décodé de la même façon, car vous ne faites que regarder les octets .

Cependant, si vous utilisiez un codage spécifique, cela vous aurait posé des problèmes pour coder/décoder des caractères non valides.

1803
Mehrdad

Cela dépend de l'encodage de votre chaîne ( ASCII , TF-8 , ...).

Par exemple:

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

Un petit échantillon pourquoi le codage est important:

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 n'est tout simplement pas équipé pour traiter des caractères spéciaux.

En interne, le framework .NET utilise TF-16 pour représenter les chaînes. Si vous voulez simplement obtenir les octets exacts utilisés par .NET, utilisez System.Text.Encoding.Unicode.GetBytes (...).

Voir Codage de caractères dans le .NET Framework (MSDN) pour plus d'informations.

1087
bmotmans

La réponse acceptée est très, très compliquée. Utilisez les classes .NET incluses pour cela:

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

Ne réinventez pas la roue si vous n'avez pas à ...

273
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

Vous devez prendre en compte l’encodage, car 1 caractère peut être représenté par 1 ou plus octets (jusqu’à environ 6), et différents encodages traiteront ces octets différemment.

Joel a posté sur ce sujet:

Le minimum absolu que chaque développeur de logiciel a absolument, doit absolument savoir sur Unicode et les jeux de caractères (sans excuses!)

91
Zhaph - Ben Duguid

C'est une question populaire. Il est important de comprendre ce que l'auteur de la question pose et qu'il est différent de ce qui constitue probablement le besoin le plus courant. Pour décourager l'utilisation abusive du code là où il n'est pas nécessaire, j'ai répondu au premier plus tard.

Besoin commun

Chaque chaîne a un jeu de caractères et un encodage. Lorsque vous convertissez un objet System.String en un tableau de System.Byte, vous disposez toujours d'un jeu de caractères et d'un codage. Dans la plupart des cas, vous sauriez quel jeu de caractères et quel encodage vous avez besoin et .NET simplifie la copie avec conversion. Il suffit de choisir le Encoding classe.

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

Il se peut que la conversion doive gérer des cas où le jeu de caractères ou l’encodage cible ne prend pas en charge un caractère figurant dans la source. Vous avez le choix: exception, substitution ou saut. La politique par défaut consiste à remplacer par "?".

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

De toute évidence, les conversions ne sont pas nécessairement sans perte!

Remarque: Pour System.String, le jeu de caractères source est Unicode.

Le seul inconvénient est que .NET utilise le nom d'un jeu de caractères pour désigner le nom d'un codage particulier de ce jeu de caractères. Encoding.Unicode devrait s'appeler Encoding.UTF16.

Voilà pour la plupart des usages. Si c'est ce dont vous avez besoin, arrêtez de lire ici. Voir le plaisir article de Joel Spolsky si vous ne comprenez pas ce qu'est un encodage.

Besoin spécifique

Maintenant, l'auteur de la question demande: "Chaque chaîne est stockée sous la forme d'un tableau d'octets, non? Pourquoi ne puis-je pas simplement avoir ces octets?"

Il ne veut aucune conversion.

De la spéc. C # :

Le traitement des caractères et des chaînes en C # utilise le codage Unicode. Le type char représente une unité de code UTF-16 et le type chaîne représente une séquence d'unités de code UTF-16.

Donc, nous savons que si nous demandons la conversion null (c'est-à-dire de UTF-16 à UTF-16), nous obtiendrons le résultat souhaité:

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

Mais pour éviter la mention des encodages, nous devons le faire d'une autre manière. Si un type de données intermédiaire est acceptable, il existe un raccourci conceptuel pour cela:

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

Cela ne nous donne pas le type de données souhaité mais La réponse de Mehrdad montre comment convertir ce tableau Char en tableau Byte en utilisant BlockCopy . Cependant, cela copie la chaîne deux fois! De plus, il utilise aussi explicitement un code spécifique au codage: le type de données System.Char.

Le seul moyen d'obtenir les octets réels dans lesquels la chaîne est stockée consiste à utiliser un pointeur. L'instruction fixed permet de prendre l'adresse des valeurs. De la spécification C #:

[For] une expression de type chaîne, ... l'initialiseur calcule l'adresse du premier caractère de la chaîne.

Pour ce faire, le compilateur écrit du code, ignore les autres parties de l'objet chaîne avec RuntimeHelpers.OffsetToStringData. Donc, pour obtenir les octets bruts, il suffit de créer un pointeur sur la chaîne et de copier le nombre d'octets nécessaires.

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

Comme @CodesInChaos l’a souligné, le résultat dépend de l’endianité de la machine. Mais l'auteur de la question n'est pas concerné par cela.

85
Tom Blodget

La première partie de votre question (comment obtenir les octets) a déjà été répondue par d'autres: regardez dans l'espace de noms System.Text.Encoding.

Je vais répondre à votre question suivante: pourquoi avez-vous besoin de choisir un encodage? Pourquoi ne pouvez-vous pas l'obtenir de la classe string elle-même?

La réponse est en deux parties.

Tout d'abord, les octets utilisés en interne par la classe string importent peu , et chaque fois que vous supposez qu'ils le font, vous êtes susceptible d'introduire un bogue.

Si votre programme appartient entièrement au monde .Net, vous n'avez pas à vous soucier de la création de tableaux d'octets pour les chaînes, même si vous envoyez des données sur un réseau. Utilisez plutôt la sérialisation .Net pour vous soucier de la transmission des données. Vous ne vous souciez plus des octets réels: le formateur de sérialisation le fait pour vous.

D'autre part, si vous envoyez ces octets à un endroit que vous ne pouvez pas garantir, les données seront extraites d'un flux sérialisé .Net? Dans ce cas, vous devez absolument vous soucier de l’encodage, car il est évident que ce système externe est important. Encore une fois, les octets internes utilisés par la chaîne importent peu: vous devez choisir un codage pour pouvoir être explicite sur ce codage du côté destinataire, même s'il s'agit du même codage que celui utilisé en interne par .Net.

Je comprends que dans ce cas, vous préféreriez peut-être utiliser les octets réels stockés par la variable de chaîne en mémoire, si possible, avec l’idée que cela pourrait économiser du travail lors de la création de votre flux d’octets. Cependant, je vous le dis, ce n'est pas important mais de vous assurer que votre sortie est bien comprise à l'autre bout, et de garantir que vous devez être explicite avec votre encodage. De plus, si vous voulez vraiment faire correspondre vos octets internes, vous pouvez déjà simplement choisir le codage Unicode et obtenir ainsi des économies de performances.

Ce qui m'amène à la deuxième partie ... choisir le codage Unicodeest dire à .Net d'utiliser le octets sous-jacents. Vous devez choisir cet encodage, car lorsque de nouveaux Unicode-Plus dernier cri sortent, le runtime .Net doit être libre d’utiliser ce nouveau modèle d’encodage amélioré sans casser votre programme. Mais, pour le moment (et l'avenir prévisible), il suffit de choisir le codage Unicode pour obtenir ce que vous voulez.

Il est également important de comprendre que votre chaîne doit être ré-écrite sur fil, ce qui implique au moins une traduction du motif binaire , même si vous utilisez un codage correspondant . L'ordinateur doit prendre en compte des éléments tels que Big vs Little Endian, l'ordre des octets du réseau, la mise en paquets, les informations de session, etc.

43
Joel Coehoorn

Juste pour démontrer que le son de Mehrdrad réponse fonctionne, son approche peut même persister = caractères de substitution non appariés (dont beaucoup s'étaient dressés contre ma réponse, mais dont tout le monde est également coupable. , par exemple System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes, ces méthodes de codage ne peuvent pas conserver les caractères de substitution élevés d800 par exemple, et celles-ci ne font que remplacer les caractères de substitution élevés par la valeur 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);
    }        
}

Sortie:

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

Essayez cela avec System.Text.Encoding.UTF8.GetBytes ou System.Text.Encoding.Unicode.GetBytes , ils remplaceront simplement les caractères de substitution élevés par une valeur fffd

À chaque fois qu'il y a un mouvement dans cette question, je pense toujours à un sérialiseur (de Microsoft ou d'un composant tiers) qui peut conserver des chaînes même s'il contient des caractères de substitution non appariés; Je google cela de temps en temps: sérialisation caractère de substitution non apparié .NET . Cela ne me fait pas perdre le sommeil, mais c'est un peu agaçant quand de temps en temps, quelqu'un commente sur ma réponse que c'est imparfait, alors que leurs réponses le sont tout autant lorsqu'il s'agit de personnages de substitution non appariés.

Darn, Microsoft aurait juste dû utiliser System.Buffer.BlockCopy dans sa BinaryFormatter

43
Michael Buen

Essayez ceci, beaucoup moins de code:

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

Eh bien, j'ai lu toutes les réponses et elles portaient sur l'utilisation de l'encodage ou sur la sérialisation qui supprime les substituts non appariés.

C'est mal quand la chaîne, par exemple, vient de SQL Server où elle a été construite à partir d'un tableau d'octets stockant, par exemple, un hachage de mot de passe. Si nous omettons quelque chose, il stockera un hachage non valide et si nous voulons le stocker en XML, nous voulons le laisser intact (car le rédacteur XML supprime une exception sur tout substitut non apparié qu'il trouve).

Donc, j'utilise Base64 l'encodage de tableaux d'octets dans de tels cas, mais bon, sur Internet, il n'y a qu'une seule solution à cela en C #, il y a un bogue et ce n'est qu'un moyen, alors ' Nous avons corrigé le bug et écrit la procédure. Vous voilà, futurs googleurs:

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

Veuillez également expliquer pourquoi le codage doit être pris en compte. Je ne peux pas simplement obtenir les octets dans lesquels la chaîne a été stockée? Pourquoi cette dépendance à l'encodage? !!!

Parce qu'il n'y a rien de tel que "les octets de la chaîne".

Une chaîne (ou plus généralement un texte) est composée de caractères: lettres, chiffres et autres symboles. C'est tout. Les ordinateurs, cependant, ne savent rien des personnages; ils ne peuvent gérer que des octets. Par conséquent, si vous souhaitez stocker ou transmettre du texte à l'aide d'un ordinateur, vous devez transformer les caractères en octets. Comment tu fais ça? Voici où les encodages viennent à la scène.

Un codage n'est rien d'autre qu'une convention pour traduire les caractères logiques en octets physiques. Le codage le plus simple et le mieux connu est le code ASCII. Il suffit de l’écrire en anglais. Pour les autres langues, vous aurez besoin d'encodages plus complets, l'une des variantes Unicode étant le choix le plus sûr de nos jours.

Donc, en bref, essayer "d'obtenir les octets d'une chaîne sans utiliser d'encodages" est aussi impossible que "écrire un texte sans utiliser de langage".

En passant, je vous recommande fortement (et à quiconque d'ailleurs) de lire ce petit morceau de sagesse: : le minimum absolu que tout développeur de logiciel doit absolument connaître de manière positive sur Unicode et les jeux de caractères (pas d'excuses) !)

22
Konamiman

C # pour convertir un string en un tableau byte:

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

Vous pouvez utiliser le code suivant pour la conversion entre tableau et chaîne d'octets.

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

Je ne suis pas sûr, mais je pense que la chaîne stocke ses informations sous la forme d'un tableau de caractères, ce qui est inefficace avec les octets. Plus précisément, la définition d'un caractère est "Représente un caractère Unicode".

prenons cet exemple:

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

Notez que la réponse Unicode est de 14 octets dans les deux cas, alors que la réponse UTF-8 n’est que de 9 octets pour le premier et de 7 pour le second.

Donc, si vous voulez seulement utiliser les octets utilisés par la chaîne, utilisez simplement Encoding.Unicode, mais ce sera inefficace avec de l'espace de stockage.

13
Ed Marty

Avec l'avènement de Span<T> publié avec C # 7.2, la technique canonique pour capturer la représentation sous-jacente en mémoire d'une chaîne dans un tableau d'octets géré est la suivante:

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

La reconversion devrait être un non-démarreur car cela signifie que vous interprétez les données d'une manière ou d'une autre, mais par souci d'exhaustivité:

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

Les noms NonPortableCast et DangerousGetPinnableReference devraient renforcer l'argument selon lequel vous ne devriez probablement pas le faire.

Notez que travailler avec _Span<T>_ nécessite l'installation de package System.Memory NuGet .

Quoi qu’il en soit, la question initiale actuelle et les commentaires de suivi impliquent que la mémoire sous-jacente n’est pas "interprétée" (ce qui, je suppose, ne signifie ni ne est modifiée ni lue). au-delà de la nécessité de l'écrire tel quel), indiquant qu'une certaine implémentation de la classe Stream devrait être utilisée au lieu de raisonner sur les données en tant que chaînes.

13
John Rasch

Le problème clé est qu'un glyphe dans une chaîne prend 32 bits (16 bits pour un code de caractère) mais qu'un octet n'a que 8 bits à épargner. Un mappage un-à-un n'existe pas, sauf si vous vous limitez aux chaînes ne contenant que des caractères ASCII. System.Text.Encoding a beaucoup de façons de mapper une chaîne en octet [], vous devez en choisir un qui évite la perte d'informations et qui est facile à utiliser par votre client quand elle doit mapper l'octet [] vers une chaîne. .

Utf8 est un encodage populaire, compact et sans perte.

11
Hans Passant

Utilisation:

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

Le résultat est:

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

Comment convertir une chaîne en octet [] en .NET (C #) sans spécifier manuellement un codage spécifique?

Un chaîne dans .NET représente le texte sous forme d'une séquence d'unités de code UTF-16; les octets sont donc déjà codés en mémoire dans UTF-16.

Réponse de Mehrdad

Vous pouvez utiliser réponse de Mehrdad , mais il utilise en réalité un encodage car les caractères sont UTF-16. Il appelle ToCharArray qui, en regardant la source , crée un char[] et y copie directement la mémoire. Ensuite, il copie les données dans un tableau d'octets qui est également alloué. Donc, sous le capot, il copie les octets sous-jacents deux fois et alloue un tableau de caractères qui n'est pas utilisé après l'appel.

Réponse de Tom Blodget

La réponse de Tom Blodget est 20-30% plus rapide que Mehrdad puisqu'il ignore l'étape intermédiaire consistant à allouer un tableau de caractères et à y copier les octets, mais vous oblige à compiler avec l'option /unsafe. Si vous ne voulez absolument pas utiliser l'encodage, je pense que c'est la voie à suivre. Si vous placez votre identifiant de cryptage dans le bloc fixed, vous n'avez même pas besoin d'allouer un tableau d'octets séparé et de copier les octets.

Aussi, pourquoi le codage devrait-il être pris en compte? Je ne peux pas simplement obtenir les octets dans lesquels la chaîne a été stockée? Pourquoi existe-t-il une dépendance aux codages de caractères?

Parce que c'est la bonne façon de le faire. string est une abstraction.

L'utilisation d'un encodage peut vous causer des problèmes si vous avez des "chaînes" avec des caractères non valides, mais cela ne devrait pas arriver. Si vous obtenez des données dans votre chaîne avec des caractères non valides, vous le faites mal. Vous devriez probablement utiliser un tableau d'octets ou un encodage Base64 pour commencer.

Si vous utilisez System.Text.Encoding.Unicode, votre code sera plus résistant. Vous n'avez pas à vous soucier de endianness du système sur lequel votre code sera exécuté. Vous n'avez pas à vous inquiéter de savoir si la prochaine version du CLR utilisera un codage interne différent.

Je pense que la question n'est pas de savoir pourquoi vous voulez vous préoccuper de l'encodage, mais pourquoi vous voulez l'ignorer et utiliser autre chose. Le codage est censé représenter l'abstraction d'une chaîne de caractères dans une séquence d'octets. System.Text.Encoding.Unicode vous donnera un petit encodage de l'ordre des octets Endian et effectuera la même chose sur tous les systèmes, maintenant et à l'avenir.

8
Jason Goemaat

moyen le plus rapide

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

EDIT comme le commentait Makotosan, c’est maintenant le meilleur moyen:

Encoding.UTF8.GetBytes(text)
8
Sunrising

L'approche la plus proche de la question du PO est celle de Tom Blodget, qui va dans l'objet et extrait les octets. Je dis plus proche parce que cela dépend de la mise en œuvre de l'objet String.

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

Bien sûr, mais c’est là que se pose l’erreur fondamentale de la question. La chaîne est un objet qui pourrait avoir une structure de données intéressante. Nous le savons déjà, car cela permet de stocker des substituts non appariés. Il pourrait stocker la longueur. Il peut garder un pointeur sur chacun des substituts "jumelés" permettant un comptage rapide. Etc. Tous ces octets supplémentaires ne font pas partie des données de caractères.

Ce que vous voulez, ce sont les octets de chaque caractère dans un tableau. Et c'est là qu'intervient le 'codage'. Par défaut, vous obtiendrez l'UTF-16LE. Si vous ne vous souciez pas des octets eux-mêmes, sauf pour l'aller-retour, vous pouvez choisir n'importe quel encodage, y compris le 'défaut', et le reconvertir plus tard (en supposant les mêmes paramètres tels que ce qui était l'encodage par défaut, les points de code, les corrections de bugs , choses permises telles que les substituts non appariés, etc.

Mais pourquoi laisser le "codage" à la magie? Pourquoi ne pas spécifier l’encodage afin de savoir quels octets vous allez obtenir?

"Why is there a dependency on character encodings?"

Encodage (dans ce contexte) signifie simplement les octets qui représentent votre chaîne. Pas les octets de l'objet chaîne. Vous vouliez que les octets dans lesquels la chaîne a été stockée - c’est là que la question a été posée naïvement. Vous vouliez les octets de chaîne dans un tableau contigu qui représente la chaîne, et non toutes les autres données binaires qu'un objet chaîne pourrait contenir.

Ce qui signifie que la façon dont une chaîne est stockée est sans importance. Vous voulez une chaîne "Encodée" en octets dans un tableau d'octets.

J'aime la réponse de Tom Bloget parce qu'il vous a conduit vers la direction "octets de l'objet chaîne". Cela dépend toutefois de la mise en œuvre, et comme il jette un œil sur les éléments internes, il peut être difficile de reconstituer une copie de la chaîne.

La réponse de Mehrdad est fausse car elle est trompeuse au niveau conceptuel. Vous avez toujours une liste d'octets codés. Sa solution particulière permet de préserver les substituts non appariés - cela dépend de la mise en œuvre. Sa solution particulière ne produirait pas les octets de la chaîne avec précision si GetBytes renvoyait la chaîne en UTF-8 par défaut.


J'ai changé d'avis à ce sujet (solution de Mehrdad) - cela ne récupère pas les octets de la chaîne; il s'agit plutôt d'obtenir les octets du tableau de caractères créé à partir de la chaîne. Indépendamment de l'encodage, le type de données char en c # a une taille fixe. Cela permet de produire un tableau d'octets de longueur cohérente et de reproduire le tableau de caractères en fonction de la taille du tableau d'octets. Donc, si l'encodage était UTF-8, mais que chaque caractère était de 6 octets pour accueillir la plus grande valeur de utf8, cela fonctionnerait quand même. Donc, en effet - le codage du caractère n'a pas d'importance.

Mais une conversion a été utilisée - chaque caractère a été placé dans une boîte de taille fixe (type de caractère de c #). Cependant, peu importe ce que cette représentation représente, ce qui est techniquement la réponse au PO. Donc, si vous allez convertir de toute façon ... Pourquoi ne pas "encoder"?

6
Gerard ONeill

Vous pouvez utiliser le code suivant pour convertir un string en un byte array dans .NET

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

Voici mon implémentation non sécurisée de la conversion de String à 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;
}

C'est beaucoup plus rapide que celle de la réponse acceptée, même si elle n'est pas aussi élégante qu'elle est. Voici mes repères Chronomètre sur 10000000 itérations:

[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

Pour l'utiliser, vous devez cocher "Autoriser le code non sécurisé" dans les propriétés de construction de votre projet. Selon .NET Framework 3.5, cette méthode peut également être utilisée en tant qu’extension de chaîne:

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

Si vous voulez vraiment une copie des octets sous-jacents d'une chaîne, vous pouvez utiliser une fonction comme celle qui suit. Cependant, vous ne devriez pas s'il vous plaît lisez la suite pour savoir pourquoi.

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

Cette fonction vous permettra d'obtenir rapidement une copie des octets sous-jacents à votre chaîne. Vous obtiendrez ces octets de la manière qu'ils encoderont sur votre système. Ce codage est presque certainement UTF-16LE mais c’est un détail d’implémentation dont vous ne devriez pas vous soucier.

Il serait plus sûr, plus simple et plus fiable de simplement appeler,

System.Text.Encoding.Unicode.GetBytes()

Selon toute vraisemblance, cela donnera le même résultat, il est plus facile de taper, et les octets vont toujours aller-retour avec un appel à

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

Utilisez simplement ceci:

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

La chaîne peut être convertie en tableau d'octets de différentes manières, en raison du fait suivant: .NET prend en charge Unicode et Unicode normalise plusieurs codages différents appelés UTF. Ils ont des longueurs de représentation d'octet différentes, mais sont équivalents en ce sens que lorsqu'une chaîne est codée, elle peut être codée dans la chaîne, mais si la chaîne est codée avec un UTF et décodée dans l'hypothèse de UTF différent si elle peut être vissée. up.

En outre, .NET prend en charge les codages non-Unicode, mais ils ne sont pas valides dans le cas général (ils ne seront valides que si un sous-ensemble limité de points de code Unicode est utilisé dans une chaîne réelle, telle que ASCII). En interne, .NET prend en charge UTF-16, mais pour la représentation du flux, UTF-8 est généralement utilisé. C'est aussi un standard de facto pour Internet.

Sans surprise, la série System.Text.Encoding, qui est une classe abstraite, prend en charge la sérialisation de chaîne dans un tableau d'octets et la désérialisation. ses classes dérivées supportent des encodages concrets: ASCIIEncoding et quatre UTF (System.Text.UnicodeEncoding prend en charge UTF-16)

Ref ce lien.

Pour la sérialisation sur un tableau d'octets, utilisez System.Text.Encoding.GetBytes. Pour l'opération inverse, utilisez System.Text.Encoding.GetChars. Cette fonction renvoie un tableau de caractères. Pour obtenir une chaîne, utilisez un constructeur de chaîne System.String(char[]).
Référez-vous à cette page.

Exemple:

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

Cela dépend de ce que vous voulez les octets POUR

En effet, comme le dit si justement Tyler dit , "Les chaînes ne sont pas des données pures. Elles ont aussi information ". Dans ce cas, l'information est un codage qui a été supposé lors de la création de la chaîne.

En supposant que vous ayez des données binaires (plutôt que du texte) stockées dans une chaîne

Ceci est basé sur le commentaire d'OP sur sa propre question et constitue la bonne question si je comprends les indications d'OP concernant le cas d'utilisation.

Stocker des données binaires dans des chaînes est probablement une mauvaise approche en raison du codage supposé mentionné ci-dessus! Quel que soit le programme ou la bibliothèque stockant ces données binaires dans un tableau string (au lieu d'un tableau byte[] qui aurait été plus approprié) a déjà perdu la bataille avant même d'avoir commencé. S'ils vous envoient les octets dans une demande/réponse REST ou tout ce qui doit transmettre des chaînes, Base64 serait la bonne approche.

Si vous avez une chaîne de texte avec un codage inconnu

Tout le monde a mal répondu à cette question incorrecte.

Si la chaîne est correcte telle quelle, choisissez simplement un codage (de préférence, commençant par UTF), utilisez la fonction System.Text.Encoding.???.GetBytes() correspondante et indiquez à qui que vous donniez les octets le codage que vous avez sélectionné.

2
NH.

code simple avec LINQ

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

EDIT: comme indiqué ci-dessous, ce n'est pas un bon moyen.

mais vous pouvez toujours l'utiliser pour comprendre LINQ avec un codage plus approprié:

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

Deux façons:

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

Et,

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

J'ai tendance à utiliser celui du bas plus souvent que le haut, je ne les ai pas évalués en termes de vitesse.

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 caractère est à la fois une clé de recherche dans une table de polices et une tradition lexicale telle que la commande, les versions majuscules et minuscules, etc.

Par conséquent, un caractère n'est pas un octet (8 bits) et un octet n'est pas un caractère. En particulier, les 256 permutations d'un octet ne peuvent pas accueillir les milliers de symboles dans certaines langues écrites, et encore moins dans toutes les langues. Par conséquent, diverses méthodes d’encodage des caractères ont été conçues. Certains encodent pour une classe de langages particulière (encodage ASCII); plusieurs langues à l'aide de pages de codes (Extended ASCII); ou, ambitieusement, toutes les langues en incluant sélectivement des octets supplémentaires selon les besoins, Unicode.

Dans un système, tel que le framework .NET, une chaîne implique un codage de caractère particulier. En .NET, cet encodage est Unicode. Étant donné que la structure lit et écrit Unicode par défaut, la gestion du codage de caractères n’est généralement pas nécessaire dans .NET.

Cependant, en règle générale, pour charger une chaîne de caractères dans le système à partir d’un flux d’octets, vous devez connaître le codage source et interpréter cette traduction, puis la traduire correctement charabia). De même, lorsqu'une chaîne est écrite dans une source externe, elle sera écrite dans un codage particulier.

0
George

De byte[] à string:

        return BitConverter.ToString(bytes);
0
Piero Alberto

Pour convertir une chaîne en octet [], utilisez la solution suivante:

string s = "abcdefghijklmnopqrstuvwxyz";
byte[] b = System.Text.UTF32Encoding.GetBytes(s);

J'espère que ça aide.

0

J'ai écrit une extension Visual Basic similaire à la réponse acceptée, mais en utilisant directement la mémoire .NET et le Marshalling pour la conversion. Elle prend en charge des plages de caractères non prises en charge par d'autres méthodes, telles que UnicodeEncoding.UTF8.GetString ou UnicodeEncoding.UTF32.GetString ou même MemoryStream and BinaryFormatter (caractères non valides tels que: ???? & 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