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

2190

Comment puis-je convertir un stringà un byte[]dans .NET (C #) sans spécifier manuellement un codage spécifique?

Je vais crypter la chaîne. Je peux le crypter sans conversion, mais j'aimerais toujours savoir pourquoi l'encodage vient jouer ici.

Aussi, pourquoi l'encodage devrait-il même être pris en considération? Je ne peux pas simplement obtenir dans quels octets la chaîne a été stockée? Pourquoi existe-t-il une dépendance sur les encodages de caractères?

Agnel Kurian
la source
23
Chaque chaîne est stockée sous forme de tableau d'octets, non? Pourquoi ne puis-je pas simplement avoir ces octets?
Agnel Kurian
135
L'encodage est ce qui mappe les caractères aux octets. Par exemple, en ASCII, la lettre «A» correspond au nombre 65. Dans un codage différent, il peut ne pas être le même. L'approche de haut niveau des chaînes prises dans le cadre .NET rend cependant cela largement hors de propos (sauf dans ce cas).
Lucas Jones
20
Pour jouer l'avocat du diable: Si vous vouliez obtenir les octets d'une chaîne en mémoire (comme .NET les utilise) et les manipuler d'une manière ou d'une autre (c'est-à-dire CRC32), et JAMAIS JAMAIS voulu le décoder de nouveau dans la chaîne d'origine ... il n'est pas simple pourquoi vous vous souciez des encodages ou comment vous choisissez lequel utiliser.
Greg
79
Personne surpris
Bevan
28
Un caractère n'est pas un octet et un octet n'est pas un caractère. Un caractère est à la fois une clé dans une table de polices et une tradition lexicale. Une chaîne est une séquence de caractères. (Les mots, paragraphes, phrases et titres ont également leurs propres traditions lexicales qui justifient leurs propres définitions de type - mais je m'égare). Comme les entiers, les nombres à virgule flottante et tout le reste, les caractères sont codés en octets. Il fut un temps où l'encodage était simple un à un: ASCII. Cependant, pour tenir compte de toute la symbologie humaine, les 256 permutations d'un octet étaient insuffisantes et des codages ont été conçus pour utiliser sélectivement plus d'octets.
George

Réponses:

1855

Contrairement aux réponses 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 dans quels octets la chaîne a été stockée» .
(Et, bien sûr, pour pouvoir reconstruire la chaîne à partir des octets.)

Pour ces objectifs, je ne comprends vraiment pas pourquoi les gens continuent de vous dire que vous avez besoin des encodages. Vous n'avez certainement PAS besoin de vous soucier des encodages pour cela.

Faites simplement 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'essaie 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 n'y a rien de mal à cette approche! S'inquiéter des encodages rend votre vie plus compliquée sans raison réelle.

Avantage supplémentaire à cette approche:

Peu importe si la chaîne contient des caractères invalides, car vous pouvez toujours obtenir les données et reconstruire la chaîne d'origine de toute façon!

Il sera codé et décodé de la même manière, car vous ne regardez que les octets .

Si vous avez utilisé un encodage spécifique, cependant, cela vous aurait posé des problèmes avec l'encodage / décodage de caractères invalides.

user541686
la source
248
Ce qui est laid au sujet de celui - ci est, que GetStringet GetBytesnécessité d'exécuter sur un système avec le même boutisme au travail. Vous ne pouvez donc pas l'utiliser pour obtenir des octets que vous souhaitez transformer en chaîne ailleurs. J'ai donc du mal à trouver une situation où je voudrais utiliser cela.
CodesInChaos
72
@CodeInChaos: Comme je l'ai dit, tout l'intérêt est que vous vouliez l'utiliser sur le même type de système, avec le même ensemble de fonctions. Sinon, vous ne devriez pas l'utiliser.
user541686
193
-1 Je garantis que quelqu'un (qui ne comprend pas les octets par rapport aux caractères) voudra convertir sa chaîne en un tableau d'octets, il le cherchera sur Google et lira cette réponse, et il fera la mauvaise chose, car dans presque tous cas, le codage EST pertinent.
artbristol
401
@artbristol: S'ils ne peuvent pas être dérangés de lire la réponse (ou les autres réponses ...), alors je suis désolé, alors il n'y a pas de meilleur moyen pour moi de communiquer avec eux. J'opte généralement pour répondre à l'OP plutôt que d'essayer de deviner ce que les autres pourraient faire de ma réponse - l'OP a le droit de savoir, et ce n'est pas parce que quelqu'un peut abuser d'un couteau que nous devons cacher tous les couteaux du monde pour nous-mêmes. Mais si vous n'êtes pas d'accord, c'est bien aussi.
user541686
185
Cette réponse est erronée à bien des niveaux, mais surtout à cause de sa déclinaison "vous n'avez PAS à vous soucier de l'encodage!". Les 2 méthodes, GetBytes et GetString sont superflues dans la mesure où elles ne sont que des réimplémentations de ce que font déjà Encoding.Unicode.GetBytes () et Encoding.Unicode.GetString (). L'instruction "tant que votre programme (ou d'autres programmes) n'essaient pas d'interpréter les octets" est également fondamentalement erronée car implicitement, cela signifie que les octets doivent être interprétés comme Unicode.
David
1108

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

Par exemple:

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

Un petit échantillon pourquoi l'encodage 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 gérer les caractères spéciaux.

En interne, le framework .NET utilise UTF-16 pour représenter les chaînes, donc si vous voulez simplement obtenir les octets exacts que .NET utilise, utilisez System.Text.Encoding.Unicode.GetBytes (...).

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

bmotmans
la source
14
Mais pourquoi le codage devrait-il être pris en considération? Pourquoi ne puis-je pas simplement obtenir les octets sans avoir à voir quel encodage est utilisé? Même si cela était nécessaire, l'objet String lui-même ne devrait-il pas savoir quel encodage est utilisé et simplement vider ce qui est en mémoire?
Agnel Kurian
57
Une chaîne .NET est toujours codée en Unicode. Utilisez donc System.Text.Encoding.Unicode.GetBytes (); pour obtenir l'ensemble d'octets que .NET utiliserait pour représenter les caractères. Mais pourquoi voudriez-vous cela? Je recommande UTF-8 en particulier lorsque la plupart des personnages se trouvent dans l'ensemble latin occidental.
AnthonyWJones
8
Aussi: les octets exacts utilisés en interne dans la chaîne n'ont pas d'importance si le système qui les récupère ne gère pas cet encodage ou le gère comme un encodage incorrect. Si tout est dans .Net, pourquoi le convertir en un tableau d'octets? Sinon, il vaut mieux être explicite avec votre encodage
Joel Coehoorn
11
@ Joel, soyez prudent avec System.Text.Encoding.Default car il peut être différent sur chaque machine exécutée. C'est pourquoi il est recommandé de toujours spécifier un encodage, tel que UTF-8.
Ash
25
Vous n'avez pas besoin des encodages à moins que vous (ou quelqu'un d'autre) n'ayez réellement l'intention d' interpréter les données, au lieu de les traiter comme un "bloc d'octets" générique. Pour des choses comme la compression, le chiffrement, etc., se soucier de l'encodage n'a pas de sens. Voir ma réponse pour un moyen de le faire sans vous soucier de l'encodage. (J'ai peut-être donné un -1 pour dire que vous devez vous soucier des encodages lorsque vous ne le faites pas, mais je ne me sens pas particulièrement méchant aujourd'hui.: P)
user541686
285

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'êtes pas obligé ...

Erik A. Brandstadmoen
la source
14
Dans le cas où la réponse acceptée est modifiée, à des fins d'enregistrement, c'est la réponse de Mehrdad à l'heure et à la date actuelles. Espérons que le PO reverra cela et acceptera une meilleure solution.
Thomas Eding
7
bon en principe, mais le codage devrait System.Text.Encoding.Unicodeêtre équivalent à la réponse de Mehrdad.
Jodrell
5
La question a été modifiée un uptillion de fois depuis la réponse d'origine, donc ma réponse est peut-être un peu dépassée. Je n'ai jamais eu l'intention de donner une exace équivalente à la réponse de Mehrdad, mais de donner une manière sensée de le faire. Mais vous avez peut-être raison. Cependant, l'expression "obtenir dans quels octets la chaîne a été stockée" dans la question d'origine est très imprécise. Stocké, où? En mémoire? Sur disque? Si en mémoire, ce System.Text.Encoding.Unicode.GetBytesserait probablement plus précis.
Erik A. Brandstadmoen
7
@AMissico, votre suggestion est boguée, sauf si vous êtes sûr que votre chaîne est compatible avec l'encodage par défaut de votre système (chaîne contenant uniquement des caractères ASCII dans le jeu de caractères hérité par défaut de votre système). Mais nulle part le PO ne le dit.
Frédéric
5
@AMissico Cependant, le programme peut donner des résultats différents sur différents systèmes . Ce n'est jamais une bonne chose. Même si c'est pour faire un hachage ou quelque chose (je suppose que c'est ce que signifie OP avec 'encrypt'), la même chaîne devrait toujours donner le même hachage.
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());
Michael Buen
la source
2
Vous pouvez utiliser la même instance BinaryFormatter pour toutes ces opérations
Joel Coehoorn
3
Très intéressant. Apparemment, il supprimera tout caractère Unicode de substitution élevé. Voir la documentation sur [BinaryFormatter ]
95

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

Joel a une publication à ce sujet:

Le minimum absolu Chaque développeur de logiciels doit absolument, positivement, connaître Unicode et les jeux de caractères (pas d'excuses!)

Zhaph - Ben Duguid
la source
6
"1 caractère pourrait être représenté par 1 ou plusieurs octets" Je suis d'accord. Je veux juste ces octets, quel que soit l'encodage de la chaîne. La seule façon dont une chaîne peut être stockée en mémoire est en octets. Même les caractères sont stockés sur 1 ou plusieurs octets. Je veux simplement mettre la main sur ces octets.
Agnel Kurian
16
Vous n'avez pas besoin des encodages à moins que vous (ou quelqu'un d'autre) n'ayez réellement l'intention d' interpréter les données, au lieu de les traiter comme un "bloc d'octets" générique. Pour des choses comme la compression, le chiffrement, etc., se soucier de l'encodage n'a pas de sens. Voir ma réponse pour un moyen de le faire sans vous soucier de l'encodage.
user541686
9
@Mehrdad - Totalement, mais la question d'origine, comme indiqué lorsque j'ai répondu au départ, ne mettait pas en garde ce que l'OP allait se passer avec ces octets après les avoir convertis, et pour les futurs chercheurs les informations qui sont pertinentes - c'est couvert par la réponse de Joel très bien - et comme vous le dites dans votre réponse: à condition de rester dans le monde .NET et d'utiliser vos méthodes pour convertir vers / depuis, vous êtes heureux. Dès que vous sortez de cela, l'encodage est important.
Zhaph - Ben Duguid
Un point de code peut être représenté par jusqu'à 4 octets. (Une unité de code UTF-32, une paire de substitution UTF-16 ou 4 octets UTF-8.) Les valeurs pour lesquelles UTF-8 aurait besoin de plus de 4 octets sont en dehors de la plage 0x0..0x10FFFF de Unicode. ;-)
DevSolar
89

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 est 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 le plus tard en premier.

Besoin commun

Chaque chaîne a un jeu de caractères et un encodage. Lorsque vous convertissez un System.Stringobjet en tableau, System.Bytevous avez toujours un jeu de caractères et un codage. Pour la plupart des utilisations, vous savez quel jeu de caractères et quel encodage vous avez besoin et .NET facilite la «copie avec conversion». Choisissez simplement la Encodingclasse appropriée .

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

La conversion peut avoir besoin de gérer les cas où le jeu de caractères ou l'encodage cible ne prend pas en charge un caractère qui se trouve dans la source. Vous avez le choix: exception, substitution ou saut. La politique par défaut consiste à remplacer un «?».

// 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.Stringle jeu de caractères source est Unicode.

La seule chose déroutante est que .NET utilise le nom d'un jeu de caractères pour le nom d'un codage particulier de ce jeu de caractères. Encoding.Unicodedevrait être appelé Encoding.UTF16.

C'est tout pour la plupart des utilisations. Si c'est ce dont vous avez besoin, arrêtez de lire ici. Consultez l'article amusant 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 forme de tableau d'octets, n'est-ce pas? Pourquoi ne puis-je pas simplement avoir ces octets?"

Il ne veut aucune conversion.

De la spécification 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 nulle (c'est-à-dire de UTF-16 en UTF-16), nous obtiendrons le résultat souhaité:

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

Mais pour éviter la mention des encodages, il faut faire autrement. 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 un tableau d'octets à l'aide de BlockCopy . Cependant, cela copie la chaîne deux fois! Et, il utilise aussi explicitement du code spécifique au codage: le type de données System.Char.

La seule façon d'accéder aux octets réels dans lesquels la chaîne est stockée est d'utiliser un pointeur. L' fixedinstruction permet de prendre l'adresse des valeurs. De la spécification C #:

[Pour] 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 sautant les autres parties de l'objet chaîne avec RuntimeHelpers.OffsetToStringData. Donc, pour obtenir les octets bruts, créez simplement un pointeur sur la chaîne et copiez 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 l'a souligné @CodesInChaos, le résultat dépend de l'endianité de la machine. Mais l'auteur de la question ne s'en préoccupe pas.

Tom Blodget
la source
3
@Jan C'est correct mais la longueur de la chaîne donne déjà le nombre d'unités de code (pas les codepoints).
Tom Blodget
1
Merci d'avoir fait remarquer cela! De MSDN: "La Lengthpropriété [de String] renvoie le nombre d' Charobjets dans cette instance, pas le nombre de caractères Unicode." Votre exemple de code est donc correct tel qu'il est écrit.
Jan Hettich
1
@supercat "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." —_ Spécification C # 5_ Bien que, oui, rien n'empêche une chaîne Unicode non valide:new String(new []{'\uD800', '\u0030'})
Tom Blodget
1
@TomBlodget: Il est intéressant de noter que si l'on prend des instances de Globalization.SortKey, extrait le KeyData, et emballe les octets résultants de chacun dans un String[deux octets par caractère, MSB d'abord ], l'appel String.CompareOrdinalaux chaînes résultantes sera beaucoup plus rapide que l'appel SortKey.Compareaux instances de SortKey, ou même en faisant appel memcmpà ces instances. Compte tenu de cela, je me demande pourquoi KeyDatarenvoie un Byte[]plutôt qu'un String?
supercat
1
Hélas, la bonne réponse, mais des années trop tard, n'aura jamais autant de voix que l'accepté. En raison de TL; DR, les gens penseront que la réponse acceptée est géniale. copiez-le et votez-le.
Martin Capodici
46

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

Je vais répondre à votre question de suivi: pourquoi avez-vous besoin de choisir un encodage? Pourquoi ne pouvez-vous pas obtenir cela de la classe de chaîne elle-même?

La réponse est en deux parties.

Tout d'abord, les octets utilisés en interne par la classe de chaîne n'ont pas d'importance , et chaque fois que vous supposez qu'ils le font, vous introduisez probablement un bogue.

Si votre programme est entièrement dans le monde .Net, vous n'avez pas à vous soucier d'obtenir des 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'un autre côté, que se passe-t-il si vous envoyez ces octets quelque part que vous ne pouvez pas garantir extraira des données d'un flux sérialisé .Net? Dans ce cas, vous devez absolument vous soucier de l'encodage, car ce système externe se soucie évidemment. Encore une fois, les octets internes utilisés par la chaîne n'ont pas d'importance: vous devez choisir un encodage pour pouvoir être explicite sur cet encodage du côté de la réception, même s'il s'agit du même encodage utilisé en interne par .Net.

Je comprends que dans ce cas, vous préférerez peut-être utiliser les octets réels stockés par la variable de chaîne en mémoire lorsque cela est possible, avec l'idée que cela pourrait économiser du travail en créant votre flux d'octets. Cependant, je vous suggère que ce n'est tout simplement pas important par rapport à vous assurer que votre sortie est comprise à l'autre extrémité, et pour 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 Unicodecodage et obtenir ces économies de performances.

Ce qui m'amène à la deuxième partie ... choisir l' Unicodeencodage, c'est dire à .Net d'utiliser les octets sous-jacents. Vous devez choisir cet encodage, car lorsque de nouveaux Unicode-Plus sortent, le runtime .Net doit être libre pour utiliser ce modèle d'encodage plus récent et meilleur sans interrompre votre programme. Mais, pour le moment (et l'avenir prévisible), le simple choix de l'encodage Unicode vous donne ce que vous voulez.

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

Joel Coehoorn
la source
9
Il existe des domaines dans .NET où vous devez obtenir des tableaux d'octets pour les chaînes. De nombreuses classes .NET Cryptrography contiennent des méthodes telles que ComputeHash () qui acceptent un tableau d'octets ou un flux. Vous n'avez pas d'autre choix que de convertir une chaîne en un tableau d'octets en premier (en choisissant un encodage), puis de l'encapsuler éventuellement dans un flux. Cependant, tant que vous choisissez un encodage (c'est-à-dire UTF8) avec un stick, il n'y a aucun problème avec cela.
Ash
44

Juste pour démontrer que son de Mehrdrad réponse œuvres, son approche peut même persister les caractères de substitution non appariés (dont beaucoup avaient portées contre ma réponse, mais dont tout le monde sont également coupables, par exemple System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; ces méthodes de codage ne peuvent pas persister la forte substitution caractères d800par exemple, et ceux qui viennent simplement remplacer les caractères de substitution élevés avec une 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);
    }        
}

Production:

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 la valeur fffd

Chaque fois qu'il y a un mouvement dans cette question, je pense toujours à un sérialiseur (que ce soit 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 ceci 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 ennuyeux quand de temps en temps quelqu'un commente ma réponse qu'elle est erronée, mais leurs réponses sont également imparfaites en ce qui concerne les caractères de substitution non appariés.

Bon sang, Microsoft aurait juste dû utiliser System.Buffer.BlockCopydans son BinaryFormatter

谢谢!

Michael Buen
la source
3
Les substituts ne doivent-ils pas apparaître par paires pour former des points de code valides? Si tel est le cas, je peux comprendre pourquoi les données seraient altérées.
dtanders
1
@dtanders Oui, c'est aussi ce que je pense, ils doivent apparaître par paires, les caractères de substitution non appariés ne se produisent que si vous les mettez délibérément sur une chaîne et les rendez non appariés. Ce que je ne sais pas, c'est pourquoi d'autres développeurs continuent de penser que nous devrions plutôt utiliser une approche prenant en compte l'encodage, car ils ont estimé que l'approche de sérialisation ( ma réponse , qui était une réponse acceptée depuis plus de 3 ans) ne gardait pas la paire. caractère de substitution intact. Mais ils ont oublié de vérifier que leurs solutions prenant en charge l'encodage ne conservent pas non plus le caractère de substitution non apparié, l'ironie ツ
Michael Buen
S'il y a une bibliothèque de sérialisation qui utilise en System.Buffer.BlockCopyinterne, tous les arguments des gens de l'encodage-plaidoyer seront sans objet
Michael Buen
2
@MichaelBuen Il me semble que le principal problème est que vous êtes en grosses lettres audacieuses disant que quelque chose n'a pas d'importance, plutôt que de dire que cela n'a pas d'importance dans leur cas. Par conséquent, vous encouragez les gens qui regardent votre réponse à commettre des erreurs de programmation de base qui causeront de la frustration aux autres à l'avenir. Les substituts non appariés ne sont pas valides dans une chaîne. Ce n'est pas un tableau de caractères, il est donc logique que la conversion d'une chaîne dans un autre format entraîne une erreur FFFDsur ce caractère. Si vous souhaitez effectuer une manipulation de chaîne manuelle, utilisez un char [] comme recommandé.
Trisped
2
@dtanders: A System.Stringest une séquence immuable de Char; .NET a toujours permis à un Stringobjet d'être construit à partir de any Char[]et d'exporter son contenu vers un Char[]contenant les mêmes valeurs, même si l'original Char[]contient des substituts non appariés.
supercat
41

Essayez ceci, beaucoup moins de code:

System.Text.Encoding.UTF8.GetBytes("TEST String");
Nathan
la source
Alors essayez ceci System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);et pleurez! Cela fonctionnera, mais System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Lengthpendant que"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
mg30rg
9
@ mg30rg: Pourquoi pensez-vous que votre exemple est étrange? Certes, dans un codage à largeur variable, tous les caractères n'ont pas la même longueur d'octets. Qu'est ce qui ne va pas avec ça?
Vlad
@Vlad Un commentaire plus valable ici, cependant, est qu'en tant que symboles unicode codés (donc, en octets), les caractères qui incluent leurs propres diacritiques donneront un résultat différent de celui des diacritiques divisés en symboles modificateurs ajoutés au caractère. Mais iirc, il existe des méthodes dans .net pour les séparer spécifiquement, afin de permettre une représentation cohérente des octets.
Nyerguds
25

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 mauvais lorsque la chaîne, par exemple, provient 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 en supprimons 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 lâche une exception sur tout substitut non apparié qu'il trouve).

J'utilise donc le codage Base64 des tableaux d'octets dans de tels cas, mais bon, sur Internet, il n'y a qu'une seule solution à cela en C #, et il y a un bug et c'est seulement dans un sens, j'ai donc corrigé le bug et réécrit procédure. 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);
}
Gman
la source
Au lieu d'utiliser votre méthode personnalisée pour convertir un tableau d'octets en base64, tout ce que vous aviez à faire était d'utiliser le convertisseur intégré: Convert.ToBase64String (arr);
Makotosan
@Makotosan merci, mais j'ai utilisé Convert.ToBase64String(arr); pour les conversions base64 byte[] (data) <-> string (serialized data to store in XML file). Mais pour obtenir l'initiale, byte[] (data)je devais faire quelque chose avec un Stringcontenant des données binaires (c'est la façon dont MSSQL me les a renvoyées). Donc les fonctions ci-dessus sont pour String (binary data) <-> byte[] (easy accessible binary data).
Gman
23

Veuillez également expliquer pourquoi le codage doit être pris en considération. Je ne peux pas simplement obtenir dans quels octets 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 entrent en scène.

Un codage n'est rien d'autre qu'une convention pour traduire des caractères logiques en octets physiques. L'encodage le plus simple et le plus connu est ASCII, et c'est tout ce dont vous avez besoin si vous écrivez en anglais. Pour d'autres langues, vous aurez besoin d'encodages plus complets, l'une des saveurs 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 de codages" est aussi impossible que "écrire un texte sans utiliser de langue".

Soit dit en passant, je vous recommande fortement (et à quiconque d'ailleurs) de lire ce petit morceau de sagesse: le minimum absolu que chaque développeur de logiciels doit absolument, positivement, connaître à propos de l'Unicode et des jeux de caractères (pas d'excuses!)

Konamiman
la source
2
Permettez-moi de clarifier: un encodage a été utilisé pour traduire "hello world" en octets physiques. Étant donné que la chaîne est stockée sur mon ordinateur, je suis sûr qu'elle doit être stockée en octets. Je veux simplement accéder à ces octets pour les enregistrer sur le disque ou pour toute autre raison. Je ne veux pas interpréter ces octets. Comme je ne veux pas interpréter ces octets, le besoin d'un encodage à ce stade est aussi déplacé que d'exiger une ligne téléphonique pour appeler printf.
Agnel Kurian
3
Mais encore une fois, il n'y a pas de concept de traduction de texte en octets physiques à moins que vous n'utilisiez un codage. Bien sûr, le compilateur stocke les chaînes en quelque sorte en mémoire - mais il utilise simplement un encodage interne que vous (ou quiconque sauf le développeur du compilateur) ne connaissez pas. Donc, quoi que vous fassiez, vous avez besoin d'un encodage pour obtenir des octets physiques à partir d'une chaîne.
Konamiman
@Agnel Kurian: Il est bien sûr vrai qu'une chaîne a un tas d'octets quelque part qui stocke son contenu (UTF-16 afair). Mais il y a une bonne raison pour vous empêcher d'y accéder: les chaînes sont immuables et si vous pouviez obtenir le tableau d'octets interne [], vous pourriez aussi le modifier. Cela rompt l'immuabilité, ce qui est vital car plusieurs chaînes peuvent partager les mêmes données. L'utilisation d'un encodage UTF-16 pour obtenir la chaîne ne fera probablement que copier les données.
ollb
2
@Gnafoo, Une copie des octets fera l'affaire.
Agnel Kurian
22

C # pour convertir un stringen bytetableau:

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
Shyam sundar shah
la source
17
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
gkrogers
la source
Mais pourquoi le codage devrait-il être pris en considération? Pourquoi ne puis-je pas simplement obtenir les octets sans avoir à voir quel encodage est utilisé? Même si cela était nécessaire, l'objet String lui-même ne devrait-il pas savoir quel encodage est utilisé et simplement vider ce qui est en mémoire?
Agnel Kurian
5
Cela ne fonctionne pas toujours. Certains caractères spéciaux peuvent se perdre en utilisant une telle méthode que j'ai trouvée difficile.
JB King
17

Vous pouvez utiliser le code suivant pour la conversion entre chaîne et tableau 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);
Jarvis Stark
la source
VUP celui-ci a résolu mon problème (octet [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd
16

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

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

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

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

Les noms NonPortableCastet DangerousGetPinnableReferencedevraient étayer l'argument selon lequel vous ne devriez probablement pas faire cela.

Notez que l'utilisation de Span<T>nécessite l'installation du package System.Memory NuGet .

Quoiqu'il en soit, la réelle question initiale et des commentaires de suivi impliquent que la mémoire sous - jacente n'est pas « interprété » (que je suppose que des moyens ne sont pas modifiés ou lire au - delà de la nécessité de l' écrire comme-est), ce qui indique qu'une certaine mise en œuvre de la Streamclasse doit être utilisé au lieu de raisonner sur les données sous forme de chaînes.

John Rasch
la source
13

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

prenez cet exemple d'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, tandis 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 juste que les octets Encoding.Unicodesoient utilisés par la chaîne, utilisez simplement , mais cela sera inefficace avec l'espace de stockage.

Ed Marty
la source
10

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 ne dispose que de 8 bits. Un mappage un à un n'existe pas, sauf si vous vous limitez aux chaînes qui ne contiennent que des caractères ASCII. System.Text.Encoding a de nombreuses façons de mapper une chaîne en octet [], vous devez en choisir une qui évite la perte d'informations et qui est facile à utiliser par votre client lorsqu'il a besoin de mapper l'octet [] en chaîne .

Utf8 est un encodage populaire, il est compact et sans perte.

Hans Passant
la source
3
UTF-8 n'est compact que si la majorité de vos caractères sont dans le jeu de caractères anglais (ASCII). Si vous aviez une longue chaîne de caractères chinois, UTF-16 serait un encodage plus compact que UTF-8 pour cette chaîne. En effet, UTF-8 utilise un octet pour coder ASCII, et 3 (ou peut-être 4) sinon.
Joel Mueller
7
Vrai. Mais, comment pouvez-vous ne pas connaître l'encodage si vous êtes familier avec la manipulation de texte chinois?
Hans Passant le
9

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
mashet
la source
OP demande spécifiquement de NE PAS spécifier un encodage ... "sans spécifier manuellement un encodage spécifique"
Ferdz
8

Moyen le plus rapide

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

EDIT comme Makotosan l'a commenté, c'est maintenant le meilleur moyen:

Encoding.UTF8.GetBytes(text)
Alessandro Annini
la source
8
ASCIIEncoding ..... n'est pas nécessaire. Il est préférable d'utiliser simplement Encoding.UTF8.GetBytes (texte).
Makotosan
8

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

Une chaîne en .NET représente le texte comme une séquence d'unités de code UTF-16, donc les octets sont déjà encodés en mémoire en UTF-16.

Réponse de Mehrdad

Vous pouvez utiliser la réponse de Mehrdad , mais elle utilise en fait 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. Il copie ensuite les données dans un tableau d'octets qui est également alloué. Donc, sous le capot, il copie deux fois les octets sous-jacents et alloue un tableau de caractères qui n'est pas utilisé après l'appel.

La réponse de Tom Blodget

La réponse de Tom Blodget est 20 à 30% plus rapide que Mehrdad car elle ignore l'étape intermédiaire d'allouer un tableau de caractères et de copier les octets, mais elle nécessite que vous compiliez avec l' /unsafeoption. Si vous ne voulez absolument pas utiliser l'encodage, je pense que c'est la voie à suivre. Si vous placez votre connexion de chiffrement dans le fixedbloc, vous n'avez même pas besoin d'allouer un tableau d'octets séparé et de copier les octets dans celui-ci.

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

Parce que c'est la bonne façon de procéder. stringest une abstraction.

L'utilisation d'un encodage peut vous poser des problèmes si vous avez des «chaînes» avec des caractères non valides, mais cela ne devrait pas se produire. Si vous obtenez des données dans votre chaîne avec des caractères non valides, vous vous trompez. 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 l' endianité du système sur lequel votre code sera exécuté. Vous n'avez pas à vous inquiéter si la prochaine version du CLR utilisera un encodage de caractères interne différent.

Je pense que la question n'est pas pourquoi vous voulez vous soucier 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 dans une séquence d'octets. System.Text.Encoding.Unicodevous donnera un petit codage de l'ordre des octets endian et effectuera la même chose sur tous les systèmes, maintenant et à l'avenir.

Jason Goemaat
la source
En fait, une chaîne en C # n'est PAS limitée à seulement UTF-16. Ce qui est vrai, c'est qu'il contient un vecteur d'unités de code 16 bits, mais ces unités de code 16 bits ne sont pas limitées à l'UTF-16 valide. Mais comme ils sont en 16 bits, vous avez besoin d'un encodage (ordre des octets) pour les convertir en 8 bits. Une chaîne peut ensuite stocker des données non Unicode, y compris du code binaire (par exemple une image bitmap). Il devient interprété comme UTF-16 uniquement dans les E / S et les formateurs de texte qui font une telle interprétation.
verdy_p
Ainsi, dans une chaîne C #, vous pouvez stocker en toute sécurité une unité de code comme 0xFFFF ou 0xFFFE, même s'il ne s'agit pas de caractères en UTF-16, et vous pouvez stocker un 0xD800 isolé non suivi d'une unité de code dans 0xDC00..0xDFFF (c'est-à-dire substituts non appariés non valides en UTF-16). La même remarque s'applique aux chaînes en Javascript / ECMAscript et Java.
verdy_p
Lorsque vous utilisez "GetBytes", bien sûr, vous ne spécifiez pas d'encodage, mais vous supposez un ordre d'octets pour obtenir les deux octets dans une spécification pour chaque unité de code stockée localement dans la chaîne. Lorsque vous créez une nouvelle chaîne à partir d'octets, vous avez également besoin d'un convertisseur, pas nécessairement UTF-8 en UTF-16, vous pouvez insérer le 0 supplémentaire dans l'octet haut ou compresser deux octets (en MSB en premier ou en LSB en premier ordre) dans la même unité de code 16 bits. Les chaînes sont alors de forme compacte pour les tableaux d'entiers 16 bits. La relation avec les "personnages" est un autre problème, en C # ce ne sont pas des types réels car ils sont toujours représentés comme des chaînes
verdy_p
7

L'approche la plus proche de la question de l'OP est celle de Tom Blodget, qui va en fait dans l'objet et extrait les octets. Je dis le plus proche car 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 l'erreur fondamentale de la question se pose. La chaîne est un objet qui pourrait avoir une structure de données intéressante. Nous le savons déjà, car il permet de stocker des substituts non appariés. Il pourrait stocker la longueur. Il pourrait garder un pointeur sur chacun des substituts «apparié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à que «l'encodage» entre en jeu. Par défaut, vous obtiendrez 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 le codage par défaut, les points de code, les corrections de bogues). , les choses autorisées telles que les substituts non appariés, etc.

Mais pourquoi laisser «l'encodage» à la magie? Pourquoi ne pas spécifier l'encodage pour savoir quels octets vous allez obtenir?

"Why is there a dependency on character encodings?"

L'encodage (dans ce contexte) signifie simplement les octets qui représentent votre chaîne. Pas les octets de l'objet chaîne. Vous vouliez 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ésentent la chaîne, et non toutes les autres données binaires qu'un objet chaîne peut contenir.

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

J'aime la réponse de Tom Bloget parce qu'il vous a emmené dans la direction des «octets de l'objet chaîne». Cela dépend de l'implémentation, et comme il regarde les composants internes, il peut être difficile de reconstituer une copie de la chaîne.

La réponse de Mehrdad est fausse parce qu'elle est trompeuse au niveau conceptuel. Vous avez toujours une liste d'octets, encodée. Sa solution particulière permet de conserver les substituts non appariés - cela dépend de l'implémentation. Sa solution particulière ne produirait pas les octets de la chaîne avec précision si elle GetBytesrenvoyait la chaîne en UTF-8 par défaut.


J'ai changé d'avis à ce sujet (la solution de Mehrdad) - cela n'obtient pas les octets de la chaîne; il récupère plutôt les octets du tableau de caractères créé à partir de la chaîne. Quel que soit le codage, le type de données char en c # est de 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 chaque caractère était de 6 octets pour accueillir la plus grande valeur utf8, cela fonctionnerait toujours. Donc en effet - l'encodage du personnage 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, quelle est cette représentation n'a pas d'importance, ce qui est techniquement la réponse au PO. Donc - si vous voulez quand même convertir ... Pourquoi ne pas 'encoder'?

Gerard ONeill
la source
Ces caractères ne sont pas pris en charge par UTF-8 ou UTF-16 ou même UTF-32 pour exapmle: 񩱠& (Char) 55906& (Char) 55655. Vous pouvez donc vous tromper et la réponse de Mehrdad est une conversion sûre sans tenir compte du type d'encodage utilisé.
Mojtaba Rezaeian
Raymon, les caractères sont déjà représentés par une valeur unicode - et toutes les valeurs unicode peuvent être représentées par tous les utf. Y a-t-il une explication plus longue de ce dont vous parlez? Dans quel codage de caractères ces deux valeurs (ou 3 ..) existent-elles?
Gerard ONeill
Ce sont des caractères non valides qui ne sont pris en charge par aucune plage de codage. Cela ne signifie pas qu'ils sont 100% inutiles. Un code qui convertit n'importe quel type de chaîne en son équivalent de tableau d'octets indépendamment des encodages n'est pas une mauvaise solution du tout et a ses propres utilisations aux occasions souhaitées.
Mojtaba Rezaeian
1
D'accord, je pense que vous ne comprenez pas le problème. Nous savons que c'est un tableau compatible Unicode - en fait, parce que c'est .net, nous savons que c'est UTF-16. Ces personnages n'existeront donc pas là-bas. Vous n'avez pas non plus lu entièrement mon commentaire sur le changement des représentations internes. Une chaîne est un objet, pas un tableau d'octets codés. Je vais donc être en désaccord avec votre dernière déclaration. Vous voulez que le code convertisse toutes les chaînes unicode en n'importe quel encodage UTF. Cela fait ce que vous voulez, correctement.
Gerard ONeill
Les objets sont une séquence de données à l'origine une séquence de bits qui décrivent un objet dans son état actuel. Ainsi, toutes les données dans les langages de programmation sont convertibles en tableau d'octets (chaque octet définit 8 bits) car vous devrez peut-être conserver un état de n'importe quel objet en mémoire. Vous pouvez enregistrer et conserver une séquence d'octets dans un fichier ou de la mémoire et la convertir en entier, bigint, image, chaîne Ascii, chaîne UTF-8, chaîne cryptée ou votre propre type de données défini après l'avoir lu sur le disque. Vous ne pouvez donc pas dire que les objets sont quelque chose de différent de la séquence d'octets.
Mojtaba Rezaeian, le
6

Vous pouvez utiliser le code suivant pour convertir un stringen un byte arraydans .NET

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

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 lire 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 fournira une copie des octets sous-jacents à votre chaîne, assez rapidement. Vous obtiendrez ces octets de quelque manière qu'ils encodent sur votre système. Cet encodage est presque certainement UTF-16LE mais c'est un détail d'implémentation dont vous ne devriez pas avoir à vous soucier.

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

System.Text.Encoding.Unicode.GetBytes()

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

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

Voici ma mise en œuvre dangereuse de Stringla Byte[]conversion:

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

Il est beaucoup plus rapide que celui qui est accepté, même s'il n'est pas aussi élégant qu'il est. Voici mes benchmarks chronomètres 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 comme extension de chaîne:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
Tommaso Belluzzo
la source
La valeur d' RuntimeHelpers.OffsetToStringDataun multiple de 8 est-elle sur les versions Itanium de .NET? Sinon, cela échouera en raison des lectures non alignées.
Jon Hanna
ne serait-il pas plus simple d'invoquer memcpy? stackoverflow.com/a/27124232/659190
Jodrell
2

Utilisez simplement ceci:

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
alireza amini
la source
2
... et perdez tous les caractères avec un saut supérieur à 127. Dans ma langue maternelle, il est parfaitement valable d'écrire "Árvíztűrő tükörfúrógép.". System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();renverra des "Árvizturo tukörfurogép."informations perdues qui ne peuvent pas être récupérées. (Et je n'ai pas encore mentionné les langues asiatiques où vous
perdriez
2

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 standardise plusieurs codages de différence appelés UTF. Ils ont des longueurs de représentation d'octets différentes mais sont équivalents en ce sens que lorsqu'une chaîne est codée, elle peut être codée en retour dans la chaîne, mais si la chaîne est codée avec un UTF et décodée dans l'hypothèse de différents UTF si elle peut être vissée vers le haut.

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

Sans surprise, la sérialisation de la chaîne en un tableau d'octets et la désérialisation sont prises en charge par la classe System.Text.Encoding, qui est une classe abstraite; ses classes dérivées prennent en charge les encodages concrets: ASCIIEncodinget quatre UTF ( System.Text.UnicodeEncodingprend en charge UTF-16)

Ref ce lien.

Pour la sérialisation vers un tableau d'octets à l'aide de System.Text.Encoding.GetBytes. Pour l'opération inverse, utilisez System.Text.Encoding.GetChars. Cette fonction renvoie un tableau de caractères, donc pour obtenir une chaîne, utilisez un constructeur de chaîne System.String(char[]).
Réf 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? :-)
Vijay Singh Rana
la source
2

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

En effet, comme l'a si bien dit Tyler , "les chaînes ne sont pas de pures données. Elles contiennent également des informations ." Dans ce cas, les informations sont un codage supposé lors de la création de la chaîne.

En supposant que vous avez 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 c'est la bonne question si je comprends les indices d'OP sur le cas d'utilisation.

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

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

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

Si la chaîne semble bonne telle System.Text.Encoding.???.GetBytes()quelle , choisissez simplement un encodage (de préférence un commençant par UTF), utilisez la fonction correspondante et dites à qui vous donnez les octets à quel encodage vous avez choisi.

NH.
la source
2

Lorsqu'on vous a demandé ce que vous comptiez faire des octets, vous avez répondu :

Je vais le crypter. Je peux le crypter sans le convertir mais j'aimerais toujours savoir pourquoi l'encodage vient jouer ici. Donnez-moi juste les octets, c'est ce que je dis.

Que vous ayez l'intention d'envoyer ces données chiffrées sur le réseau, de les recharger en mémoire ultérieurement ou de les transférer dans un autre processus, vous avez clairement l'intention de les déchiffrer à un moment donné. Dans ce cas, la réponse est que vous définissez un protocole de communication. Un protocole de communication ne doit pas être défini en termes de détails d'implémentation de votre langage de programmation et de son runtime associé. Il y a plusieurs raisons à cela:

  • Vous devrez peut-être communiquer avec un processus implémenté dans une langue ou un runtime différent. (Cela peut inclure un serveur fonctionnant sur une autre machine ou envoyer la chaîne à un client de navigateur JavaScript, par exemple.)
  • Le programme peut être réimplémenté dans une langue ou un runtime différent à l'avenir.
  • L'implémentation .NET peut modifier la représentation interne des chaînes. Vous pensez peut-être que cela semble farfelu, mais cela s'est réellement produit dans Java 9 pour réduire l'utilisation de la mémoire. Il n'y a aucune raison pour que .NET ne puisse pas emboîter le pas. Skeet suggère que l'UTF-16 n'est probablement pas optimal aujourd'hui, car les emoji et autres blocs d'Unicode ont également besoin de plus de 2 octets pour représenter, ce qui augmente la probabilité que la représentation interne puisse changer à l'avenir.

Pour communiquer (avec un processus complètement disparate ou avec le même programme à l'avenir), vous devez définir votre protocole strictement pour minimiser la difficulté de travailler avec lui ou de créer accidentellement des bogues. Dépendre de la représentation interne de .NET n'est pas une définition stricte, claire ou même garantie d'être cohérente. Un encodage standard est une définition stricte qui ne vous fera pas défaut à l'avenir.

En d'autres termes, vous ne pouvez pas satisfaire votre exigence de cohérence sans spécifier un codage.

Vous pouvez certainement choisir d'utiliser UTF-16 directement si vous trouvez que votre processus fonctionne beaucoup mieux car .NET l'utilise en interne ou pour toute autre raison, mais vous devez choisir explicitement cet encodage et effectuer ces conversions explicitement dans votre code plutôt que de dépendre sur l'implémentation interne de .NET.

Choisissez donc un encodage et utilisez-le:

using System.Text;

// ...

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

Comme vous pouvez le voir, il est également en fait moins de code d'utiliser simplement les objets de codage intégrés que d'implémenter vos propres méthodes de lecture / écriture.

jpmc26
la source
1

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 comparés pour la vitesse.


la source
4
Et les caractères multi-octets?
Agnel Kurian
c.ToByte () est privé: S
Khodor
@AgnelKurian Msdn dit "Cette méthode renvoie une valeur d'octet non signé qui représente le code numérique de l'objet Char qui lui est transmis. Dans le .NET Framework, un objet Char est une valeur de 16 bits. Cela signifie que la méthode est appropriée pour renvoyer les codes numériques des caractères dans la plage de caractères ASCII ou dans les plages Unicode C0 Controls et Basic Latin, et C1 Controls et Latin-1 Supplement, de U + 0000 à 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
user1120193
la source