Подтвердить что ты не робот

Знаки порядка байтов StreamWriter и UTF-8

У меня проблема с метками StreamWriter и Byte Order. В документации, как представляется, указывается, что кодировка Encoding.UTF8 имеет метки байтового порядка, но когда файлы записываются, некоторые имеют метки, а другие - нет.

Я создаю запись потока следующим образом:

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

Любые идеи о том, что может произойти, будут оценены.

4b9b3361

Ответ 1

Как уже указывал кто-то, вызов без аргумента кодирования делает трюк. Однако, если вы хотите быть явным, попробуйте следующее:

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

Ключом является создание нового UTF8Encoding (false) вместо использования Encoding.UTF8Encoding. Это, чтобы контролировать, нужно ли добавить спецификацию или нет.

Это то же самое, что вызывать StreamWriter без аргумента кодирования, внутренне он просто делает то же самое.

Ответ 2

Единственный раз, когда я видел, что конструктор не добавляет спецификацию UTF-8, - если поток не находится в позиции 0, когда вы его вызываете. Например, в приведенном ниже коде спецификация не записывается:

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

Как говорили другие, если вы используете конструктор StreamWriter(stream), не указав кодировку, вы не увидите спецификацию.

Ответ 3

Проблема связана с тем, что вы используете статическое свойство UTF8 в классе Encoding.

Когда метод GetPreamble вызывается в экземпляре класса Encoding, возвращаемого свойством UTF8, он возвращает знак порядка байтов ( байтовый массив из трех символов) и записывается в поток до того, как любое другое содержимое будет записано в поток (предполагая новый поток).

Вы можете избежать этого, создав экземпляр класса UTF8Encoding, например:

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

В соответствии с документацией для конструктора defaultlessless (выделение мое):

Этот конструктор создает экземпляр, который не предоставляет отметку порядка байтов Unicode и не генерирует исключение, когда обнаружена некорректная кодировка.

Это означает, что вызов GetPreamble будет возвращать пустой массив, и поэтому в базовый поток не будет записана спецификация.

Ответ 4

Мой ответ основан на HelloSam, который содержит всю необходимую информацию. Только я верю, что запрашивает OP, - как убедиться, что в файл выбрана спецификация.

Поэтому вместо передачи false в UTF8Encoding ctor вам нужно передать true.

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

Попробуйте приведенный ниже код, откройте полученные файлы в шестнадцатеричном редакторе и посмотрите, какая из них содержит спецификацию, а какая нет.

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Привет");
        }
    }

Ответ 5

Используете ли вы один и тот же конструктор StreamWriter для каждого файла? Поскольку в документации указано:

Чтобы создать StreamWriter с использованием кодировки UTF-8 и спецификации, рассмотрите возможность использования конструктора, который задает кодировку, например StreamWriter (String, Boolean, Encoding).

Я был в подобной ситуации некоторое время назад. В итоге я использовал метод Stream.Write вместо StreamWriter и написал результат Encoding.GetPreamble() перед тем, как написать Encoding.GetBytes(stringToWrite)

Ответ 6

Похоже, что если файл уже существует и не содержит спецификации, он не будет содержать спецификацию при перезаписывании, другими словами StreamWriter сохраняет спецификацию (или ее отсутствие) при перезаписывании файла.

Ответ 7

Я нашел этот ответ полезным (спасибо @Philipp Grathwohl и @Nik), но в моем случае я использую FileStream для выполнения задачи, поэтому код, который генерирует спецификацию, выглядит следующим образом:

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

Ответ 8

Не могли бы вы показать ситуацию, когда она не производит? Единственный случай, когда преамбула отсутствует, которую я могу найти, - это когда ничто никогда не писалось писателю (Джим Мишель, похоже, нашел другую, логичную и, скорее всего, вашу проблему, см. Ответ).

Мой тестовый код:

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