Marques d’ordre des octets StreamWriter et UTF-8

J’ai un problème avec les marques de commande StreamWriter et Byte. La documentation semble indiquer que le codage Encoding.UTF8 a des repères d’ordre d’octet activés, mais que certains fichiers ont des repères alors que d’autres ne le sont pas.

Je crée le programme d’écriture de stream de la manière suivante:

this.Writer = new StreamWriter(this.Stream, System.Text.Encoding.UTF8); 

Toutes les idées sur ce qui pourrait se passer seraient appréciées.

Comme quelqu’un l’a déjà souligné, l’appel sans l’argument d’encodage fait l’affaire. Cependant, si vous voulez être explicite, essayez ceci:

 using (var sw = new StreamWriter(this.Stream, new UTF8Encoding(false))) 

La clé est de construire un nouvel UTF8Encoding (false) au lieu d’utiliser Encoding.UTF8Encoding. C’est pour contrôler si la nomenclature doit être ajoutée ou non.

C’est la même chose que d’appeler StreamWriter sans l’argument d’encodage, en interne c’est juste faire la même chose.

Le problème est dû au fait que vous utilisez la propriété statique UTF8 sur la classe Encoding .

Lorsque la méthode GetPreamble est appelée sur l’instance de la classe Encoding renvoyée par la propriété UTF8 , elle renvoie la marque d’ordre d’octets (le tableau d’octets de trois caractères) et est écrite dans le stream avant que tout autre contenu soit écrit dans le stream (en supposant que un nouveau stream).

Vous pouvez éviter cela en créant vous-même l’instance de la classe UTF8Encoding , comme ceci:

 // As before. this.Writer = new StreamWriter(this.Stream, // Create yourself, passing false will prevent the BOM from being written. new System.Text.UTF8Encoding()); 

Selon la documentation du constructeur par défaut sans paramètre (c’est moi qui souligne):

Ce constructeur crée une instance qui ne fournit pas de marque d’ordre d’octet Unicode et ne lance pas d’exception lorsqu’un codage non valide est détecté.

Cela signifie que l’appel à GetPreamble renverra un tableau vide et, par conséquent, aucune nomenclature ne sera écrite dans le stream sous-jacent.

La seule fois où j’ai vu ce constructeur ne pas append la nomenclature UTF-8 est si le stream n’est pas à la position 0 lorsque vous l’appelez. Par exemple, dans le code ci-dessous, la nomenclature n’est pas écrite:

 using (var s = File.Create("test2.txt")) { s.WriteByte(32); using (var sw = new StreamWriter(s, Encoding.UTF8)) { sw.WriteLine("hello, world"); } } 

Comme d’autres l’ont dit, si vous utilisez le constructeur StreamWriter(stream) , sans spécifier le codage, vous ne verrez pas la nomenclature.

Ma réponse est basée sur celle de HelloSam qui contient toutes les informations nécessaires. Seulement je crois que ce que OP demande, c’est comment s’assurer que la nomenclature est émise dans le fichier.

Donc, au lieu de transmettre false à UTF8Encoding, vous devez transmettre true.

  using (var sw = new StreamWriter("text.txt", new UTF8Encoding(true))) 

Essayez le code ci-dessous, ouvrez les fichiers résultants dans un éditeur hexadécimal et voyez lequel contient la nomenclature et lequel ne contient pas.

 class Program { static void Main(ssortingng[] args) { const ssortingng nobomtxt = "nobom.txt"; File.Delete(nobomtxt); using (Stream stream = File.OpenWrite(nobomtxt)) using (var writer = new StreamWriter(stream, new UTF8Encoding(false))) { writer.WriteLine("HelloПривет"); } const ssortingng bomtxt = "bom.txt"; File.Delete(bomtxt); using (Stream stream = File.OpenWrite(bomtxt)) using (var writer = new StreamWriter(stream, new UTF8Encoding(true))) { writer.WriteLine("HelloПривет"); } } 

Utilisez-vous le même constructeur de StreamWriter pour chaque fichier? Parce que la documentation dit:

Pour créer un StreamWriter à l’aide du codage UTF-8 et d’une nomenclature, envisagez d’utiliser un constructeur qui spécifie le codage, tel que StreamWriter (Ssortingng, Boolean, Encoding).

J’étais dans une situation similaire il y a quelque temps. J’ai fini par utiliser la méthode Stream.Write au lieu de StreamWriter et j’ai écrit le résultat de Encoding.GetPreamble() avant d’écrire Encoding.GetBytes(ssortingngToWrite)

J’ai trouvé cette réponse utile (grâce à @Philipp Grathwohl et @Nik), mais dans mon cas, j’utilise FileStream pour accomplir la tâche. Le code qui génère la nomenclature va donc comme ceci:

 using (FileStream vStream = File.Create(pfilePath)) { // Creates the UTF-8 encoding with parameter "encoderShouldEmitUTF8Identifier" set to true Encoding vUTF8Encoding = new UTF8Encoding(true); // Gets the preamble in order to attach the BOM var vPreambleByte = vUTF8Encoding.GetPreamble(); // Writes the preamble first vStream.Write(vPreambleByte, 0, vPreambleByte.Length); // Gets the bytes from text byte[] vByteData = vUTF8Encoding.GetBytes(pTextToSaveToFile); vStream.Write(vByteData, 0, vByteData.Length); vStream.Close(); } 

Il semblerait que si le fichier existait déjà et ne contenait pas de nomenclature, il ne contiendrait pas de nomenclature en cas d’écrasement, c’est-à-dire que StreamWriter conservait la nomenclature (ou son absence) lors du remplacement d’un fichier.

Pourriez-vous s’il vous plaît montrer une situation où il ne le produit pas? Le seul cas où le préambule n’est pas présent est celui où rien n’est écrit à l’auteur (Jim Mischel semble avoir trouvé un autre problème, logique et plus probable, voir sa réponse).

Mon code de test:

 var stream = new MemoryStream(); using(var writer = new StreamWriter(stream, System.Text.Encoding.UTF8)) { writer.Write('a'); } Console.WriteLine(stream.ToArray() .Select(b => b.ToSsortingng("X2")) .Aggregate((i, a) => i + " " + a) );