c# - unidades - StreamWriter y UTF-8 Byte Order Marks
representa texto como una secuencia de unidades de codigo utf-16 c# (8)
Tengo un problema con StreamWriter y Byte Order Marks. La documentación parece indicar que la codificación Encoding.UTF8 tiene marcas de orden de bytes habilitadas, pero cuando se escriben archivos, algunas tienen las marcas mientras que otras no.
Estoy creando el escritor de secuencias de la siguiente manera:
this.Writer = new StreamWriter(this.Stream, System.Text.Encoding.UTF8);
Cualquier idea sobre lo que podría estar pasando sería apreciada.
¿Podría por favor mostrar una situación donde no lo produce? El único caso donde el preámbulo no está presente que puedo encontrar es cuando no se escribe nada al escritor (Jim Mischel parece haber encontrado otro, lógico y más probable que sea su problema, vea la respuesta).
Mi código de prueba:
var stream = new MemoryStream();
using(var writer = new StreamWriter(stream, System.Text.Encoding.UTF8))
{
writer.Write(''a'');
}
Console.WriteLine(stream.ToArray()
.Select(b => b.ToString("X2"))
.Aggregate((i, a) => i + " " + a)
);
¿Utiliza el mismo constructor de StreamWriter para cada archivo? Porque la documentación dice:
Para crear un StreamWriter con codificación UTF-8 y una lista de materiales, considere usar un constructor que especifique la codificación, como StreamWriter (String, Boolean, Encoding).
Estuve en una situación similar hace un tiempo. Terminé usando el método Stream.Write
lugar del StreamWriter y escribí el resultado de Encoding.GetPreamble()
antes de escribir Encoding.GetBytes(stringToWrite)
Como alguien ya señaló eso, llamar sin el argumento de codificación hace el truco. Sin embargo, si quieres ser explícito, prueba esto:
using (var sw = new StreamWriter(this.Stream, new UTF8Encoding(false)))
La clave es construir una nueva codificación UTF8 (falsa), en lugar de usar Encoding.UTF8Encoding. Eso es para controlar si se debe agregar o no BOM.
Esto es lo mismo que llamar a StreamWriter sin el argumento de codificación, internamente solo hace lo mismo.
El problema se debe al hecho de que está utilizando la propiedad estática UTF8
en la clase de Encoding
.
Cuando se GetPreamble
método GetPreamble
en la instancia de la clase de Encoding
devuelta por la propiedad UTF8
, devuelve la marca de orden de bytes (la matriz de bytes de tres caracteres) y se escribe en la secuencia antes de que cualquier otro contenido se escriba en la secuencia (suponiendo una nueva corriente).
Puede evitar esto creando la instancia de la clase UTF8Encoding
usted mismo, así:
// As before.
this.Writer = new StreamWriter(this.Stream,
// Create yourself, passing false will prevent the BOM from being written.
new System.Text.UTF8Encoding());
Según la documentación para el constructor sin parámetros predeterminado (el énfasis es mío):
Este constructor crea una instancia que no proporciona una marca de orden de bytes Unicode y no lanza una excepción cuando se detecta una codificación no válida.
Esto significa que la llamada a GetPreamble
devolverá una matriz vacía y, por lo tanto, no se escribirá una lista de materiales en la secuencia subyacente.
Encontré esta respuesta útil (gracias a @Philipp Grathwohl y @Nik), pero en mi caso estoy usando FileStream para llevar a cabo la tarea, por lo que el código que genera la lista de materiales es el siguiente:
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();
}
La única vez que he visto que el constructor no agrega la lista de materiales UTF-8 es si la secuencia no está en la posición 0 cuando la llama. Por ejemplo, en el código a continuación, la lista de materiales no se escribe:
using (var s = File.Create("test2.txt"))
{
s.WriteByte(32);
using (var sw = new StreamWriter(s, Encoding.UTF8))
{
sw.WriteLine("hello, world");
}
}
Como han dicho otros, si está utilizando el StreamWriter(stream)
, sin especificar la codificación, no verá la lista de materiales.
Mi respuesta se basa en la de HelloSam, que contiene toda la información necesaria. Solo creo que lo que está solicitando OP es cómo asegurarse de que la lista de materiales se emita en el archivo.
Entonces, en lugar de pasar falso a UTF8Encoding ctor, debe pasar true.
using (var sw = new StreamWriter("text.txt", new UTF8Encoding(true)))
Pruebe el siguiente código, abra los archivos resultantes en un editor hexadecimal y vea cuál contiene la lista de materiales y cuál no.
class Program
{
static void Main(string[] args)
{
const string 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 string bomtxt = "bom.txt";
File.Delete(bomtxt);
using (Stream stream = File.OpenWrite(bomtxt))
using (var writer = new StreamWriter(stream, new UTF8Encoding(true)))
{
writer.WriteLine("HelloПривет");
}
}
Parece que si el archivo ya existía y no contenía BOM, entonces no contendrá BOM cuando se sobrescriba, en otras palabras, StreamWriter conserva BOM (o su ausencia) al sobrescribir un archivo.