« Chaînes » et « octets » : pourquoi est-ce un casse-tête éternel pour le programmeur ?
\nImaginez la situation : vous écrivez une application réseau en C#, vous envoyez des données au serveur, et en sortie, vous obtenez un charabia de caractères. Ou vous chiffrez un mot de passe, et de l'autre côté, on ne peut pas le déchiffrer. Cela vous dit quelque chose ? La racine du mal réside souvent dans une conversion incorrecte d'une chaîne en tableau d'octets (et vice versa).
\nLa question « Converting string to byte array in C# » sur Stack Overflow a rassemblé des milliers de vues, et ce n'est pas un hasard. C'est la base sur laquelle reposent la gestion des fichiers, la cryptographie, la sérialisation et les communications réseau. Sans comprendre ce sujet, vous allez répéter les mêmes erreurs. Mettons les choses au clair une fois pour toutes sur la façon de procéder correctement.
\n\nPourquoi ne peut-on pas simplement convertir une chaîne en octets ?
\nUne approche naïve serait d'utiliser BitConverter.GetBytes() pour un caractère ? Non. Le problème est que la chaîne est une abstraction composée de caractères (char, 2 octets en C#). Alors qu'un tableau d'octets est une donnée brute qui dépend de l'encodage.
Le même caractère « A » dans l'encodage UTF-8 occupe 1 octet, en UTF-16 — 2 octets, et en UTF-32 — 4 octets. Si vous choisissez le mauvais encodage, les données seront perdues. Souvenez-vous de la règle principale : toujours spécifier l'encodage explicitement.
\n\nLa méthode la plus populaire : Encoding.UTF8.GetBytes()
\nC'est ce que vous utiliserez dans 90 % des cas. L'UTF-8 est le standard de facto pour le web, le JSON et la plupart des API.
\n\nusing System;\nusing System.Text;\n\nclass Program\n{\n static void Main()\n {\n string original = "Привет, мир! 🌍";\n \n // Convertir la chaîne en tableau d'octets (UTF-8)\n byte[] utf8Bytes = Encoding.UTF8.GetBytes(original);\n \n // Conversion inverse (pour vérification)\n string restored = Encoding.UTF8.GetString(utf8Bytes);\n \n Console.WriteLine($"Chaîne originale : {original}");\n Console.WriteLine($"Tableau d'octets (longueur {utf8Bytes.Length}) : {BitConverter.ToString(utf8Bytes)}");\n Console.WriteLine($"Chaîne restaurée : {restored}");\n }\n}\n\nRésultat :
\nChaîne originale : Привет, мир! 🌍\nTableau d'octets (longueur 18) : D0-9F-D1-80-D0-B8-D0-B2-D0-B5-D1-82-2C-20-D0-BC-D0-B8-D1-80-21-20-F0-9F-8C-8D\nChaîne restaurée : Привет, мир! 🌍\n\nNotez que l'emoji « 🌍 » occupe 4 octets (F0-9F-8C-8D). L'UTF-8 peut gérer n'importe quel caractère Unicode, contrairement aux encodages obsolètes.
\n\nAutres encodages courants
\n- \n
- Encoding.ASCII — uniquement les caractères 7 bits (0-127). Ne convient pas pour le cyrillique ou les caractères spéciaux — tout ce qui dépasse 127 sera transformé en '?' \n
- Encoding.Unicode (également appelé UTF-16LE) — utilisé en interne par C# pour stocker les chaînes. Chaque caractère occupe 2 octets (ou 4 pour les paires de substitution). \n
- Encoding.UTF32 — chaque caractère fait exactement 4 octets. Gourmand en ressources, mais prévisible. \n
- Encoding.Default — encodage système (ANSI). À éviter ! Il varie d'un ordinateur à l'autre. \n
Conversion avec BitConverter et MemoryStream
\nParfois, il est nécessaire d'obtenir une représentation « brute » de la chaîne sous forme de tableau de caractères (char). Ce n'est pas la même chose que les octets dans un encodage, mais cela peut être utile pour un travail de bas niveau.
\n\nstring text = "C#";\n// Obtenir le tableau de char\nchar[] chars = text.ToCharArray();\n// Convertir chaque char en 2 octets (Little-Endian)\nbyte[] rawBytes = new byte[chars.Length * sizeof(char)];\nBuffer.BlockCopy(chars, 0, rawBytes, 0, rawBytes.Length);\n\nConsole.WriteLine(BitConverter.ToString(rawBytes)); // 43-00-23-00\n\nCette méthode donne un tableau encodé en UTF-16LE (tel qu'il est stocké en mémoire). Utilisez-la uniquement si vous savez exactement ce que vous faites.
\n\nCas particulier : Base64
\nIl