programming/netfx/tips/unicode_encoding_bom/index.wiki.txt

current previous
1,241 1,137
~
%smdncms%(set-metadata,title,EncodingクラスとBOMありなしの制御)
%smdncms%(set-metadata,title,System.Text.EncodingのBOMありなしの制御)
 
%smdncms%(set-metadata,keywords,C#,Encoding,BOM,UTF,Unicode,StreamWriter,BinaryWriter,GetPreamble,GetBytes,File.WriteAllText,XmlWriter,XmlWriterSettings,XmlDocument,XDocument)
%smdncms%(set-metadata,keywords,C#,Encoding,BOM,UTF,Unicode,StreamWriter,BinaryWriter,GetPreamble,GetBytes,File.WriteAllText,XmlWriter,XmlWriterSettings,XmlDocument,XDocument)
 
%smdncms%(set-metadata,tags,lang/c#,api/.net)
%smdncms%(set-metadata,tags,lang/c#,api/.net)
 

        

        
~
&msdn(netfx,type,System.Text.Encoding){System.Text.Encoding};クラスとBOMのあり/なしの指定方法について。 また&msdn(netfx,type,System.IO.StreamWriter){System.IO.StreamWriter};内部の動作や、Encodingを指定できるクラスでのBOM出力の指定、その他BOM関連の処理について。
&msdn(netfx,type,System.Text.Encoding){System.Text.Encoding};クラスとBOM(Byte Order Mark)のあり/なしの指定方法について。 また&msdn(netfx,type,System.IO.StreamWriter){System.IO.StreamWriter};内部の動作やEncodingを指定できるクラスとBOM出力の指定について。
 

        

        
 
$relevantdocs
$relevantdocs
 

        

        
 
-[[programming/netfx/stream/2_streamreader_streamwriter]]
-[[programming/netfx/stream/2_streamreader_streamwriter]]
-
-[[programming/netfx/environment/1_process#LocaleAndCulture_DefaultEncoding]]
-
-[[programming/netfx/tips/encoding_fallback]]
 
-[[programming/netfx/xml/1_xmlwritersettings]]
-[[programming/netfx/xml/1_xmlwritersettings]]
+
-[[programming/netfx/tips/encoding_fallback]]
+
-[[programming/netfx/environment/1_process#LocaleAndCulture_DefaultEncoding]]
 

        

        
 
$relevantdocs$
$relevantdocs$
 

        

        
 
#adunit
#adunit
 

        

        
~
*概略 [#summary]
*StreamWriterでのBOM出力
~
StreamWriterではデフォルトでUTF-8が用いられるほか、StreamWriterコンストラクタの引数&var{encoding};に&msdn(netfx,member,System.Text.Encoding.UTF8){Encoding.UTF8プロパティ};、&msdn(netfx,member,System.Text.Encoding.Default){Encoding.Defaultプロパティ};、&msdn(netfx,type,System.Text.UTF8Encoding){UTF8Encodingクラス};のインスタンスを指定することによりUTF-8で書き込むことができる。
結論から述べると、StreamWriterがBOMを出力するかどうかは、StreamWriterコンストラクタの引数&var{encoding};に指定する値によって次のように変わる。
 

        

        
~
このとき、StreamWriterがBOM('''Byte Order Mark''')を出力するかどうかは、&var{encoding};に指定する値によって次のように変わる。
|*StreamWriterコンストラクタのencodingパラメータとBOMの出力
-
|~encodingパラメータ|~BOM|h
-
|(指定なし)|出力しない|
-
|Encoding.UTF8|出力する|
-
|new UTF8Encoding(&color(orangered){true};)|出力する|
-
|new UTF8Encoding(&color(orangered){false};)|出力しない|
 

        

        
~
|*StreamWriterコンストラクタの引数&var{encoding};とBOM出力のありなし
$samplecode(lang=c#,copyright-year=2016,StreamWriterコンストラクタに指定するencodingパラメータとBOM出力の違い)
+
|~引数&var{encoding};に指定する値|~BOM出力|h
+
|(指定なし)|なし|
+
|``Encoding.UTF8``|あり|
+
|``Encoding.Default`` (.NET Core/.NET 5以降)|なし|
+
|``UTF8Encoding(&var{encoderShouldEmitUTF8Identifier};: ''true'')``|あり|
+
|``UTF8Encoding(&var{encoderShouldEmitUTF8Identifier};: ''false'')``|なし|
+

         
+
$samplecode(lang=c#-9.0,copyright-year=2020,StreamWriterコンストラクタの引数encodingとBOM出力のありなし)
 
#code{{
#code{{
 
using System;
using System;
 
using System.IO;
using System.IO;
 
using System.Text;
using System.Text;
 

        

        
~
foreach (var createStreamWriter in new Func<Stream, StreamWriter>[] {
class Sample {
~
  // 引数encodingに指定する値を変えてStreamWriterを作成する
  static void Main()
~
  s => new(s), // encodingの指定なし
  {
~
  s => new(s, encoding: Encoding.UTF8),
    const string text = "日本語";
~
  s => new(s, encoding: Encoding.Default), // ⚠.NET FrameworkのEncoding.DefaultはUTF-8ではなくShift_JIS等のANSIコードページとなる

         
~
  s => new(s, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)),
    using (var stream = new MemoryStream()) {
~
  s => new(s, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)),
      // encodingを指定しない場合
~
}) {
      using (var writer = new StreamWriter(stream)) {
~
  using var stream = new MemoryStream();
        writer.Write(text);
~
  const string text = "日本語";
      }
~

         
      Console.WriteLine(BitConverter.ToString(stream.ToArray()));
~
  using (var writer = createStreamWriter(stream)) {
    }
~
    writer.Write(text);

         
-
    var encodings = new Encoding[] {
-
      Encoding.UTF8,
-
      new UTF8Encoding(true),
-
      new UTF8Encoding(false),
-
    };
-

         
-
    foreach (var encoding in encodings) {
-
      using (var stream = new MemoryStream()) {
-
        // encodingを指定した場合
-
        using (var writer = new StreamWriter(stream, encoding)) {
-
          writer.Write(text);
-
        }
-
        Console.WriteLine(BitConverter.ToString(stream.ToArray()));
-
      }
-
    }
 
  }
  }
+

         
+
  // StreamWriterが出力したバイト列を16進形式で表示
+
  Console.WriteLine(BitConverter.ToString(stream.ToArray()));
 
}
}
 
}}
}}
~
$samplecode(lang=vb)
$samplecode$
~
#code{{

         
~
Option Infer On
#prompt(実行結果,emphasize=brace){{
+

         
+
Imports System
+
Imports System.IO
+
Imports System.Text
+

         
+
Class Sample
+
  Shared Sub Main()
+
    ' 引数encodingに指定する値を変えてStreamWriterを作成する
+
    For Each createStreamWriter In New Func(Of Stream, StreamWriter)() {
+
      Function(s) New StreamWriter(s), ' encodingの指定なし
+
      Function(s) New StreamWriter(s, encoding := Encoding.UTF8),
+
      Function(s) New StreamWriter(s, encoding := Encoding.Default), ' ⚠.NET FrameworkのEncoding.DefaultはUTF-8ではなくShift_JIS等のANSIコードページとなる
+
      Function(s) New StreamWriter(s, encoding := New UTF8Encoding(encoderShouldEmitUTF8Identifier := True)),
+
      Function(s) New StreamWriter(s, encoding := New UTF8Encoding(encoderShouldEmitUTF8Identifier := False))
+
    }
+
      Using stream As New MemoryStream()
+
        Const text As String = "日本語"
+

         
+
        Using writer = createStreamWriter(stream)
+
          writer.Write(text)
+
        End Using
+

         
+
        ' StreamWriterが出力したバイト列を16進形式で表示
+
        Console.WriteLine(BitConverter.ToString(stream.ToArray()))
+
      End Using
+
    Next
+
  End Sub
+
End Class
+
}}
+
$samplecode
+
#prompt(emphasize=brace){{
 
E6-97-A5-E6-9C-AC-E8-AA-9E
E6-97-A5-E6-9C-AC-E8-AA-9E
 
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
+
E6-97-A5-E6-9C-AC-E8-AA-9E
 
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
 
E6-97-A5-E6-9C-AC-E8-AA-9E
E6-97-A5-E6-9C-AC-E8-AA-9E
 
}}
}}
-
(上記実行結果における``EF-BB-BF``のシーケンスがBOM)
 

        

        
~
(強調した``EF-BB-BF``のシーケンスがUTF-8のBOM)
UTF-8以外の場合も、以下のようにUTF8Encoding(またはEncoding.UTF8)を指定した場合と同様の動作となる。 BOM付きとなるかどうかで分類すると次のようになる。
+
$samplecode$
+

         
+
UTF-8以外の場合も同様の動作となる。 BOMあり/なしで分類すると次のようになる。 Encodingクラスのプロパティとして用意されているものは、&msdn(netfx,member,System.Text.Encoding.Default){Encoding.Default};を除くと''すべてBOMあり''となる。
+

         
+
|*StreamWriterコンストラクタの引数&var{encoding};とBOM出力のありなし
+
|~エンコーディング|>|~引数&var{encoding};に指定する値|h
+
|~|~BOMあり|~BOMなし|h
+
|~UTF-8|``Encoding.UTF8``&br;``UTF8Encoding(&var{encoderShouldEmitUTF8Identifier};: ''true'')``|``Encoding.Default`` (.NET Core/.NET 5以降)&br;``UTF8Encoding(&var{encoderShouldEmitUTF8Identifier};: ''false'')``|
+
|~UTF-16&br;Little Endian|``Encoding.Unicode``&br;``UnicodeEncoding(&var{bigEndian};: false, &var{byteOrderMark};: ''true'')``|``UnicodeEncoding(&var{bigEndian};: false, &var{byteOrderMark};: ''false'')``|
+
|~UTF-16&br;Big Endian|``Encoding.BigEndianUnicode``&br;``UnicodeEncoding(&var{bigEndian};: true, &var{byteOrderMark};: ''true'')``|``UnicodeEncoding(&var{bigEndian};: true, &var{byteOrderMark};: ''false'')``|
+
|~UTF-32&br;Little Endian|``Encoding.UTF32``&br;``UTF32Encoding(&var{bigEndian};: false, &var{byteOrderMark};: ''true'')``|``UTF32Encoding(&var{bigEndian};: false, &var{byteOrderMark};: ''false'')``|
+
|~UTF-32&br;Big Endian|``UTF32Encoding(&var{bigEndian};: true, &var{byteOrderMark};: ''true'')``|``UTF32Encoding(&var{bigEndian};: true, &var{byteOrderMark};: ''false'')``|
+

         
 

        

        
~
*EncodingとBOMのあり/なし、StreamWriter等におけるBOM書き込みの動作
|*StreamWriterコンストラクタのencodingパラメータとBOMの出力
-
|~エンコーディング|>|~BOM|h
-
|~|~あり|~なし|h
-
|~UTF-8|Encoding.UTF8&br;new UTF8Encoding(&color(orangered){true};)|new UTF8Encoding(&color(orangered){false};)|
-
|~UTF-16 Little Endian|Encoding.Unicode&br;new UnicodeEncoding(false, &color(orangered){true};)|new UnicodeEncoding(false, &color(orangered){false};)|
-
|~UTF-16 Big Endian|Encoding.BigEndianUnicode&br;new UnicodeEncoding(true, &color(orangered){true};)|new UnicodeEncoding(true, &color(orangered){false};)|
-
|~UTF-32 Little Endian|Encoding.UTF32&br;new UTF32Encoding(false, &color(orangered){true};)|new UTF32Encoding(false, &color(orangered){false};)|
-
|~UTF-32 Big Endian|new UTF32Encoding(true, &color(orangered){true};)|new UTF32Encoding(true, &color(orangered){false};)|
 

        

        
+
&msdn(netfx,type,System.Text.Encoding){Encodingクラス};および派生クラスでは、そのエンコーディングでのBOMを表すバイト列(以降'''preamble'''と表記)を取得することができる。 preambleは、&msdn(netfx,member,System.Text.Encoding.Preamble){Preambleプロパティ};(.NET Standard 2.1/.NET Core 2.1以降)あるいは&msdn(netfx,member,System.Text.Encoding.GetPreamble){GetPreambleメソッド};(.NET Framework 1.1以降)から取得できる。 これらはどちらも同じ内容のバイト列を返す。
 

        

        
~
UTF-8を扱うクラス&msdn(netfx,type,System.Text.UTF8Encoding){UTF8Encoding};においては、コンストラクタの引数&var{encoderShouldEmitUTF8Identifier};に``true``を指定すると、preambleとして''UTF-8のBOM''が設定される。 逆に``false``を指定するとpreambleとして''空のバイト列''が設定される。
**StreamWriterでBOMが書き込まれる条件
-
MSDNでは、.NET Framework 3.5までStreamWriterコンストラクタの説明で以下のように記述されていた。 (.NET Framework 4.0以降のStreamWriterコンストラクタの説明では記載されていない。)
 

        

        
~
他方、&msdn(netfx,type,System.IO.StreamWriter){StreamWriter};など書き込みを行うクラスでは、Encodingからpreambleを取得して出力する。 そのため、例えばStreamWriterがBOMを出力するかどうかは、StreamWriterに与えられた''Encodingが何らかのpreambleを返すかどうか''によって決まる。
$blockquote(cite=https://msdn.microsoft.com/ja-jp/library/3aadshsx%28v=vs.90%29.aspx,cite-title=StreamWriter コンストラクタ (Stream, Encoding))
~

         
Encoding.Default 以外を指定した場合、バイト順マーク (BOM) がファイルに書き込まれます。
+
この動作をより具体的に述べると次のようになる。 StreamWriterは、ストリームへの最初の書き込みを行う前にまずEncodingからpreambleを取得し、その内容をストリームへ書き込む。 このとき、''preambleに何らかのバイト列が設定されていれば、それがBOMとして書き込まれる''ことになる。 preambleとして空の配列が設定されている場合は、当然ストリームには何も書き込まれないため、結果としてBOMは書き込まれないことになる。
+

         
+
$blockquote(cite=https://msdn.microsoft.com/ja-jp/vstudio/aa569610.aspx#Question2,cite-title=.NET Framework Developer Center:System.System.IO に関する FAQ - テキスト ファイルのエンコード方式を調べる方法はありますか。)
+
StreamWriter クラスも Encoding::GetPreamble() メソッドを呼び出し、テキスト ファイルの先頭にこのバイト列を書き込みます。これは優れた機能です。ユーザーがテキスト ファイルのエンコード方式をはっきり特定できるからです。ただし、弊社の多くの開発者は C 言語の知識を持っているため、テキスト ファイルの先頭に UTF-8 の Unicode BOM があると混乱してしまいました。また、Unicode に対応していないエディタ (vi、以前のバージョンの Emacs など) では扱いにくいこともあります。このため、StreamWriter クラスで既定で使用される UTF8Encoding では、GetPreamble メソッドから空のバイト列が返されます。UTF-8 ファイルに Unicode BOM を書き込むには、コード内で Encoding.UTF8 を明示的に指定します。
 
$blockquote$
$blockquote$
 

        

        
-
これを見ると、Encoding.Default以外(例えばEncoding.UTF8)を指定するとBOMが書き込まれるように読み取れる。
 

        

        
~

         
$samplecode(lang=c#,copyright-year=2016,StreamWriterにEncoding.Default以外を指定した場合のBOMの出力)
+
UTF8Encodingと同様に、UTF-16を扱う&msdn(netfx,type,System.Text.UnicodeEncoding){UnicodeEncodingクラス};・UTF-32を扱う&msdn(netfx,type,System.Text.UTF32Encoding){UTF32Encodingクラス};の場合も、コンストラクタの引数&var{byteOrderMark};でpreambleとして設定するバイト列をBOMとするか空のバイト列とするかを指定することができる。 BOMが定義されないShift_JISなどのEncodingでは、preambleは空のバイト列となる。
+

         
+
StreamWriter以外のクラスやメソッドを使った書き込みの場合も、Encodingのpreambleを書き込む動作になっていれば、それによってBOM出力のありなしが決まる。 ([[#System.IO.File.WriteAllText]]、[[#System.Xml]])
+

         
+
一方、BinaryWriterはEncodingを指定できるものの、preambleは無視される(書き込まれない)ため、常にBOMなしで出力される。 ([[#System.IO.BinaryWriter]])
+

         
+
なお、&msdn(netfx,member,System.Text.Encoding.GetBytes){Encoding.GetBytesメソッド};などの文字列からバイト列等に変換するメソッドでは、preambleを含まない結果を返す。 (常にBOMなしとなる)
+

         
+

         
+
**EncodingがBOMあり/なしかどうか知る方法
+
あるEncodingがBOMを出力するかどうかは、Preambleプロパティ・GetPreambleメソッドが返すpreambleの内容・長さによって判断できる。 そのため、これを調べることによって、EncodingやStreamWriterがBOMを出力するかどうか事前に知ることができる。
+

         
+
$samplecode(lang=c#,copyright-year=2020,EncodingがBOMを出力するかどうか調べる)
 
#code{{
#code{{
 
using System;
using System;
-
using System.IO;
 
using System.Text;
using System.Text;
 

        

        
 
class Sample {
class Sample {
 
  static void Main()
  static void Main()
 
  {
  {
~
    var encodings = new[] {
    var text = "日本語";
+
      Encoding.UTF8,            // UTF-8 BOM
+
      Encoding.Default,         // UTF-8 non-BOM
+
      new UTF8Encoding(true),   // UTF-8 BOM
+
      new UTF8Encoding(false),  // UTF-8 non-BOM
+
      Encoding.Unicode,                   // UTF-16 Little Endian BOM
+
      new UnicodeEncoding(false, false),  // UTF-16 Little Endian non-BOM
+
      shift_jis,                // Shift_JIS
+
    };
 

        

        
~
    foreach (var encoding in encodings) {
    using (var stream = new MemoryStream()) {
~
      var preamble = encoding.GetPreamble();
      // Encodingを指定しない
-
      using (var writer = new StreamWriter(stream)) {
-
        writer.Write(text);
-
      }
-
      Console.WriteLine(BitConverter.ToString(stream.ToArray()));
-
    }
 

        

        
~
      Console.WriteLine(
    using (var stream = new MemoryStream()) {
~
        "{0,12} {1,3} <{2}>",
      // Encoding.UTF8を指定する
~
        encoding.WebName, // encodingのweb nameを表示
      using (var writer = new StreamWriter(stream, Encoding.UTF8)) {
~
        preamble.Length == 0 ? string.Empty : "BOM", // preambleの長さからBOMのありなしを判定
        writer.Write(text);
~
        BitConverter.ToString(preamble) // preambleの内容を表示
      }
~
      );
      Console.WriteLine(BitConverter.ToString(stream.ToArray()));
 
    }
    }
 
  }
  }
+

         
+
  static readonly Encoding shift_jis =
+
#if NETFRAMEWORK
+
    Encoding.GetEncoding("shift_jis");
+
#else
+
    // `dotnet add sample.csproj package System.Text.Encoding.CodePages`
+
    CodePagesEncodingProvider.Instance.GetEncoding("shift_jis");
+
#endif
 
}
}
 
}}
}}
~
$samplecode(lang=vb)
$samplecode$
+
#code{{
+
Option Infer On
+

         
+
Imports System
+
Imports System.IO
+
Imports System.Text
+

         
+
Class Sample
+
  Shared Sub Main()
+
    Dim encodings = New Encoding() {
+
      Encoding.UTF8,            ' UTF-8 BOM
+
      Encoding.Default,         ' UTF-8 non-BOM
+
      New UTF8Encoding(True),   ' UTF-8 BOM
+
      New UTF8Encoding(False),  ' UTF-8 non-BOM
+
      Encoding.Unicode,                   ' UTF-16 Little Endian BOM
+
      New UnicodeEncoding(False, False),  ' UTF-16 Little Endian non-BOM
+
      shift_jis                 ' Shift_JIS
+
    }
 

        

        
~
    For Each encoding In encodings
#prompt(実行結果,emphasize=brace){{
~
      Dim preamble = encoding.GetPreamble()
E6-97-A5-E6-9C-AC-E8-AA-9E
~

         
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
+
      Console.WriteLine(
+
        "{0,12} {1,3} <{2}>",
+
        encoding.WebName, ' encodingのweb nameを表示
+
        If(preamble.Length = 0, String.Empty, "BOM"), ' preambleの長さからBOMのありなしを判定
+
        BitConverter.ToString(preamble) ' preambleの内容を表示
+
      )
+
    Next
+
  End Sub
+

         
+
#If NETFRAMEWORK Then
+
    Shared ReadOnly shift_jis As Encoding = Encoding.GetEncoding("shift_jis")
+
#Else
+
    ' `dotnet add sample.vbproj package System.Text.Encoding.CodePages`
+
    Shared ReadOnly shift_jis As Encoding = CodePagesEncodingProvider.Instance.GetEncoding("shift_jis")
+
#End If
+
End Class
+
}}
+
$samplecode
+
#prompt{{
+
       utf-8 BOM <EF-BB-BF>
+
       utf-8     <>
+
       utf-8 BOM <EF-BB-BF>
+
       utf-8     <>
+
      utf-16 BOM <FF-FE>
+
      utf-16     <>
+
   shift_jis     <>
 
}}
}}
+
$samplecode$
+

         
+

         
 

        

        
~
*StreamReaderにおける文字コードの自動判別とBOMあり/なしの判別
しかし実際には、次の例のようにEncoding.Default以外を指定しても、コンストラクタで&var{encoderShouldEmitUTF8Identifier};にfalseを指定したUTF8Encodingの場合はBOMが書き込まれない。
+
&msdn(netfx,type,System.IO.StreamReader){StreamReader};では、コンストラクタの引数&var{detectEncodingFromByteOrderMarks};に``true``を指定すると、ストリーム先頭のBOMから文字コードの自動判別を行うようになる。
 

        

        
~
自動判別された文字コードは&msdn(netfx,member,System.IO.StreamReader.CurrentEncoding){CurrentEncodingプロパティ};に反映されるが、''BOMがない場合を含め''自動判別できなかった場合は''BOMありの''UTF-8が設定される。 またPeek/Readなどのメソッドは、BOMを除いた上で読み込んだ内容を返す。 このため、StreamReaderからは''読み込んだストリームがBOMあり/なしかどうかを判別することができない''。
$samplecode(lang=c#,copyright-year=2016,encoderShouldEmitUTF8Identifierに指定する値によるStreamWriterのBOM出力の違い)
+

         
+
$samplecode(lang=c#,copyright-year=2020,StreamReader.CurrentEncodingプロパティからストリームのpreambleの取得を試行する(すべてBOMありとなる))
 
#code{{
#code{{
 
using System;
using System;
 
using System.IO;
using System.IO;
244,968 140,448
 
class Sample {
class Sample {
 
  static void Main()
  static void Main()
 
  {
  {
~
    var encodings = new Encoding[] {
    var text = "日本語";
+
      new UTF8Encoding(encoderShouldEmitUTF8Identifier: true),
+
      new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
+
      new UnicodeEncoding(bigEndian: true, byteOrderMark: true),
+
      new UnicodeEncoding(bigEndian: true, byteOrderMark: false),
+
      new UnicodeEncoding(bigEndian: false, byteOrderMark: true),
+
      new UnicodeEncoding(bigEndian: false, byteOrderMark: false),
+
      new UTF32Encoding(bigEndian: true, byteOrderMark: true),
+
      new UTF32Encoding(bigEndian: true, byteOrderMark: false),
+
      new UTF32Encoding(bigEndian: false, byteOrderMark: true),
+
      new UTF32Encoding(bigEndian: false, byteOrderMark: false),
+
      shift_jis,
+
    };
+
    const string text = "日本語";
+

         
+
    foreach (var encoding in encodings) {
+
      // 各種Encoding+BOMあり/なしでMemoryStreamに書き込む
+
      Console.Write($"{encoding.WebName,12} {(encoding.Preamble.Length == 0 ? string.Empty : "BOM"),3} => ");
 

        

        
~
      using var stream = new MemoryStream();
    using (var stream = new MemoryStream()) {
~

         
      // encoderShouldEmitUTF8Identifierがfalseの(BOMを出力しない)UTF8Encodingを指定する
~
      using (var writer = new StreamWriter(stream, encoding, leaveOpen: true)) {
      using (var writer = new StreamWriter(stream, new UTF8Encoding(false))) {
 
        writer.Write(text);
        writer.Write(text);
 
      }
      }
-
      Console.WriteLine(BitConverter.ToString(stream.ToArray()));
-
    }
 

        

        
~
      stream.Position = 0L;
    using (var stream = new MemoryStream()) {
~

         
      // encoderShouldEmitUTF8Identifierがtrueの(BOMを出力する)UTF8Encodingを指定する
~
      // BOMによるEncodingの自動判別を有効にしてMemoryStreamから読み込む
      using (var writer = new StreamWriter(stream, new UTF8Encoding(true))) {
~
      using (var reader = new StreamReader(stream, detectEncodingFromByteOrderMarks: true)) {
        writer.Write(text);
+
        reader.Peek(); // 自動判別されたEncodingを参照するには、まずPeek/Readを行う必要がある
+

         
+
        // 自動判別されたEncodingを参照する
+
        var e = reader.CurrentEncoding;
+

         
+
        Console.WriteLine($"{e.WebName,12} {(e.Preamble.Length == 0 ? string.Empty : "BOM"),3}");
 
      }
      }
-
      Console.WriteLine(BitConverter.ToString(stream.ToArray()));
 
    }
    }
 
  }
  }
+

         
+
  static readonly Encoding shift_jis =
+
#if NETFRAMEWORK
+
    Encoding.GetEncoding("shift_jis");
+
#else
+
    // `dotnet add sample.csproj package System.Text.Encoding.CodePages`
+
    CodePagesEncodingProvider.Instance.GetEncoding("shift_jis");
+
#endif
+
}
+
}}
+
$samplecode(lang=vb)
+
#code{{
+
Option Infer On
+

         
+
Imports System
+
Imports System.IO
+
Imports System.Text
+

         
+
Class Sample
+
  Shared Sub Main()
+
    Dim encodings = New Encoding() {
+
      New UTF8Encoding(encoderShouldEmitUTF8Identifier := True),
+
      New UTF8Encoding(encoderShouldEmitUTF8Identifier := False),
+
      New UnicodeEncoding(bigEndian := True, byteOrderMark := True),
+
      New UnicodeEncoding(bigEndian := True, byteOrderMark := False),
+
      New UnicodeEncoding(bigEndian := False, byteOrderMark := True),
+
      New UnicodeEncoding(bigEndian := False, byteOrderMark := False),
+
      New UTF32Encoding(bigEndian := True, byteOrderMark := True),
+
      New UTF32Encoding(bigEndian := True, byteOrderMark := False),
+
      New UTF32Encoding(bigEndian := False, byteOrderMark := True),
+
      New UTF32Encoding(bigEndian := False, byteOrderMark := False),
+
      shift_jis
+
    }
+

         
+
    Const text As String = "日本語"
+

         
+
    For Each encoding In encodings
+
      ' 各種Encoding+BOMあり/なしでMemoryStreamに書き込む
+
      Console.Write($"{encoding.WebName,12} {If(encoding.Preamble.Length = 0, String.Empty, "BOM"),3} => ")
+

         
+
      Using stream As New MemoryStream()
+
        Using writer As New StreamWriter(stream, encoding, leaveOpen := True)
+
          writer.Write(text)
+
        End Using
+

         
+
        stream.Position = 0L
+

         
+
        ' BOMによるEncodingの自動判別を有効にしてMemoryStreamから読み込む
+
        Using reader As New StreamReader(stream, detectEncodingFromByteOrderMarks := True)
+
          reader.Peek() ' 自動判別されたEncodingを参照するには、まずPeek/Readを行う必要がある
+

         
+
          ' 自動判別されたEncodingを参照する
+
          Dim e = reader.CurrentEncoding
+

         
+
          Console.WriteLine($"{e.WebName,12} {If(e.Preamble.Length = 0, String.Empty, "BOM"),3}")
+
        End Using
+
      End Using
+
    Next
+
  End Sub
+

         
+
#If NETFRAMEWORK Then
+
    Shared ReadOnly shift_jis As Encoding = Encoding.GetEncoding("shift_jis")
+
#Else
+
    ' `dotnet add sample.vbproj package System.Text.Encoding.CodePages`
+
    Shared ReadOnly shift_jis As Encoding = CodePagesEncodingProvider.Instance.GetEncoding("shift_jis")
+
#End If
+
End Class
+
}}
+
$samplecode
+
#prompt{{
+
       utf-8 BOM =>        utf-8 BOM
+
       utf-8     =>        utf-8 BOM
+
    utf-16BE BOM =>     utf-16BE BOM
+
    utf-16BE     =>        utf-8 BOM
+
      utf-16 BOM =>       utf-16 BOM
+
      utf-16     =>        utf-8 BOM
+
    utf-32BE BOM =>     utf-32BE BOM
+
    utf-32BE     =>        utf-8 BOM
+
      utf-32 BOM =>       utf-32 BOM
+
      utf-32     =>        utf-8 BOM
+
   shift_jis     =>        utf-8 BOM
+
}}
+
$samplecode$
+

         
+
$remarks
+
StreamクラスでEncodingの自動判別と''BOMを維持した''読み込みを行う例については[[#BomTransparentStream]]を参照。
+
$remarks$
+

         
+

         
+

         
+
*各クラスにおけるBOM出力の動作と制御
+
以下は書き込みを行うクラスにおけるBOM出力の動作と、BOMありなしを指定する方法について。
+

         
+
**System.IO名前空間 [#System.IO]
+

         
+
***StreamWriter (追記モード) [#System.IO.StreamWriter]
+
コンストラクタの引数&var{append};に``true``を指定して追記モードで書き込みを行う場合、ファイルが存在しない場合(新規作成となる場合)は''Encodingのpreambleに応じてBOMが出力される''。 一方、追記となる場合はpreambleによらず''BOMの追記は行われない''。
+

         
+
上書きモード(&var{append}; = ``false``)でのStreamWriterの動作は[[前述のとおり>#summary]]。
+

         
+
$samplecode(lang=c#-9.0,copyright-year=2020,追記モードのStreamWriterに指定するEncodingとBOM出力の有無の違い)
+
#code{{
+
using System;
+
using System.IO;
+
using System.Text;
+

         
+
const string path = "out.txt";
+
const string text = "日本語";
+

         
+
foreach (var createStreamWriter in new Func<StreamWriter>[] {
+
  // 引数encodingに指定する値を変えて、追記モード(append: true)のStreamWriterを作成する
+
  () => new(path, append: true), // encodingの指定なし
+
  () => new(path, append: true, encoding: Encoding.UTF8),
+
  () => new(path, append: true, encoding: Encoding.Default), // ⚠.NET FrameworkのEncoding.DefaultはUTF-8ではなくShift_JIS等のANSIコードページとなる
+
  () => new(path, append: true, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)),
+
  () => new(path, append: true, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)),
+
}) {
+
  if (File.Exists(path))
+
    File.Delete(path); // ファイルを削除しておく
+

         
+
  // 新規に作成したファイルへの書き込みとなる場合
+
  using (var writer = createStreamWriter()) {
+
    writer.Write(text);
+
  }
+

         
+
  // StreamWriterが出力したバイト列を16進形式で表示
+
  Console.Write("{0,-40} => ", BitConverter.ToString(File.ReadAllBytes(path)));
+

         
+
  // 既存のファイルへの追記となる場合
+
  using (var writer = createStreamWriter()) {
+
    writer.Write(text);
+
  }
+

         
+
  // StreamWriterが出力したバイト列を16進形式で表示
+
  Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(path)));
 
}
}
 
}}
}}
 
$samplecode$
$samplecode$
 

        

        
 
#prompt(実行結果,emphasize=brace){{
#prompt(実行結果,emphasize=brace){{
+
E6-97-A5-E6-9C-AC-E8-AA-9E               => E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E      => {{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
E6-97-A5-E6-9C-AC-E8-AA-9E               => E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E      => {{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
E6-97-A5-E6-9C-AC-E8-AA-9E               => E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
}}
+

         
+
$relevantdocs
+

         
+
-[[programming/netfx/stream/2_streamreader_streamwriter#StreamWriter]]
+
-[[programming/netfx/stream/2_streamreader_streamwriter#StreamWriter_Append]]
+

         
+
$relevantdocs$
+

         
+

         
+

         
+
***File.WriteAllText [#System.IO.File.WriteAllText]
+
&msdn(netfx,member,System.IO.File.WriteAllText){File.WriteAllText};などのFileクラスの書き込みメソッドでは、[[StreamWriterと同様の動作>#summary]]でBOMが出力される。
+

         
+
$samplecode(lang=c#-9.0,copyright-year=2020,File.WriteAllTextメソッドに指定するEncodingとBOM出力の有無の違い)
+
#code{{
+
using System;
+
using System.IO;
+
using System.Text;
+

         
+
const string contents = "日本語";
+
const string path = "out.txt";
+

         
+
foreach (var writeAllText in new Action[] {
+
  // 引数encodingに指定する値を変えてFile.WriteAllTextを呼び出す
+
  () => File.WriteAllText(path, contents), // encodingの指定なし
+
  () => File.WriteAllText(path, contents, encoding: Encoding.UTF8),
+
  () => File.WriteAllText(path, contents, encoding: Encoding.Default), // ⚠.NET FrameworkのEncoding.DefaultはUTF-8ではなくShift_JIS等のANSIコードページとなる
+
  () => File.WriteAllText(path, contents, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)),
+
  () => File.WriteAllText(path, contents, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)),
+
}) {
+
  writeAllText();
+

         
+
  // File.WriteAllTextが出力したバイト列を16進形式で表示
+
  Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(path)));
+
}
+
}}
+
$samplecode
+
#prompt(emphasize=brace){{
+
E6-97-A5-E6-9C-AC-E8-AA-9E
+
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
 
E6-97-A5-E6-9C-AC-E8-AA-9E
E6-97-A5-E6-9C-AC-E8-AA-9E
 
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
+
E6-97-A5-E6-9C-AC-E8-AA-9E
 
}}
}}
+
$samplecode$
 

        

        
~
$relevantdocs
**Encoding.GetPreambleとBOM、StreamWriter内部の動作
~

         
このようにStreamWriterでBOMが書き込まれるかどうかは、UTF8Encoding(およびUnicodeEncoding、UTF32Encoding)のコンストラクタに指定する値によって決まる。
+
-[[programming/netfx/filesystem/2_filereadwrite#System.IO.File_Write]]
+

         
+
$relevantdocs$
 

        

        
-
より具体的には、StreamWriterがどのようなBOMを出力するかは&msdn(netfx,member,System.Text.Encoding.GetPreamble){Encoding.GetPreambleメソッド};が返す値次第となる。 このメソッドは、エンコーディングに対応するBOMのバイト列を返す。 &var{encoderShouldEmitUTF8Identifier};にfalseになっていれば、このメソッドは空のバイト列を返す。 StreamWriterは、ストリームへ書き込む前にまずEncoding.GetPreambleメソッドを呼び出してBOMを取得し、その内容をストリームへ書き込む。 このとき、BOMを出力しないEncodingではEncoding.GetPreambleメソッドは空のバイト列を返すため、当然ストリームにもBOMは書き込まれない。
 

        

        
-
$blockquote(cite=https://msdn.microsoft.com/ja-jp/vstudio/aa569610.aspx#Question2,cite-title=.NET Framework Developer Center:System.System.IO に関する FAQ - テキスト ファイルのエンコード方式を調べる方法はありますか。)
-
StreamWriter クラスも Encoding::GetPreamble() メソッドを呼び出し、テキスト ファイルの先頭にこのバイト列を書き込みます。これは優れた機能です。ユーザーがテキスト ファイルのエンコード方式をはっきり特定できるからです。ただし、弊社の多くの開発者は C 言語の知識を持っているため、テキスト ファイルの先頭に UTF-8 の Unicode BOM があると混乱してしまいました。また、Unicode に対応していないエディタ (vi、以前のバージョンの Emacs など) では扱いにくいこともあります。このため、StreamWriter クラスで既定で使用される UTF8Encoding では、GetPreamble メソッドから空のバイト列が返されます。UTF-8 ファイルに Unicode BOM を書き込むには、コード内で Encoding.UTF8 を明示的に指定します。
-
$blockquote$
 

        

        
~
***File.AppendAllText [#System.IO.File.AppendAllText]
この動作はUTF8Encoding以外のクラスでも同様なので、EncodingがBOMを出力するかどうかを事前に知りたい場合はEncoding.GetPreambleメソッドが返す値を調べればよい。
+
&msdn(netfx,member,System.IO.File.AppendAllText){File.AppendAllText};など、Fileクラスの追記による書き込みを行うメソッドでは、[[追記モードのStreamWriterと同様の動作>#System.IO.StreamWriter]]となる。
 

        

        
~
$samplecode(lang=c#-9.0,copyright-year=2020,File.AppendAllTextメソッドに指定するEncodingとBOM出力の有無の違い)
$samplecode(lang=c#,copyright-year=2016,EncodingがBOMを出力するかどうか調べる)
 
#code{{
#code{{
 
using System;
using System;
+
using System.IO;
 
using System.Text;
using System.Text;
 

        

        
~
const string contents = "日本語";
class Sample {
~
const string path = "out.txt";
  static void Main()
-
  {
-
    var encodings = new[] {
-
      Encoding.UTF8,            // UTF-8 BOM
-
      new UTF8Encoding(true),   // UTF-8 BOM
-
      new UTF8Encoding(false),  // UTF-8 non-BOM
-
      Encoding.Unicode,                   // UTF-16 Little Endian BOM
-
      new UnicodeEncoding(false, false),  // UTF-16 Little Endian non-BOM
-
      Encoding.GetEncoding("shift_jis"),
-
    };
 

        

        
~
foreach (var appendAllText in new Action[] {
    foreach (var encoding in encodings) {
~
  // 引数encodingに指定する値を変えてFile.AppendAllTextを呼び出す
      // encodingが出力するBOMを取得
~
  () => File.AppendAllText(path, contents), // encodingの指定なし
      var preamble = encoding.GetPreamble();
+
  () => File.AppendAllText(path, contents, encoding: Encoding.UTF8),
+
  () => File.AppendAllText(path, contents, encoding: Encoding.Default), // ⚠.NET FrameworkのEncoding.DefaultはUTF-8ではなくShift_JIS等のANSIコードページとなる
+
  () => File.AppendAllText(path, contents, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)),
+
  () => File.AppendAllText(path, contents, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)),
+
}) {
+
  if (File.Exists(path))
+
    File.Delete(path); // ファイルを削除しておく
 

        

        
~
  appendAllText(); // 新規に作成したファイルへの書き込みとなる場合
      Console.WriteLine("{0} {1}",
~

         
                        encoding.WebName,
~
  // File.AppendAllTextが出力したバイト列を16進形式で表示
                        preamble.Length == 0 ? "non-BOM" : "BOM");
~
  Console.Write("{0,-40} => ", BitConverter.ToString(File.ReadAllBytes(path)));
    }
~

         
  }
+
  appendAllText(); // 既存のファイルへの追記となる場合
+

         
+
  // File.AppendAllTextが出力したバイト列を16進形式で表示
+
  Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(path)));
 
}
}
 
}}
}}
~
$samplecode
$samplecode$
+
#prompt(emphasize=brace){{
+
E6-97-A5-E6-9C-AC-E8-AA-9E               => E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E      => {{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
E6-97-A5-E6-9C-AC-E8-AA-9E               => E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E      => {{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
+
E6-97-A5-E6-9C-AC-E8-AA-9E               => E6-97-A5-E6-9C-AC-E8-AA-9E-E6-97-A5-E6-9C-AC-E8-AA-9E
 

        

        
-
#prompt(実行結果){{
-
utf-8 BOM
-
utf-8 BOM
-
utf-8 non-BOM
-
utf-16 BOM
-
utf-16 non-BOM
-
shift_jis non-BOM
 
}}
}}
+
$samplecode$
+

         
+
$relevantdocs
+

         
+
-[[programming/netfx/filesystem/2_filereadwrite#System.IO.File_Append]]
+

         
+
$relevantdocs$
+

         
 

        

        
-
**StreamWriter・BinaryWriterでのBOM出力 [#StreamWriter_BinaryWriter]
-
StreamWriterがBOMを出力するようにするには、Encoding.GetPreambleメソッドがBOMを返す(&var{encoderShouldEmitUTF8Identifier};にtrueを指定した)Encodingのインスタンスを渡す必要がある。
 

        

        
-
一方、BinaryWriterなどはEncoding.GetPreambleメソッドの戻り値に関わらずBOMを出力しない。 また、Encoding.GetBytesも常にBOMを含まないバイト列を返す。 そのため、BinaryWriterやEncoding.GetBytesでBOMを出力させたい場合は、別途Encoding.GetPreambleメソッドでBOMを取得して書き込むなどする必要がある。
 

        

        
~
***BinaryWriter [#System.IO.BinaryWriter]
次のサンプルでは、UTF8Encoding、UnicodeEncoding、UTF32Encodingの各コンストラクタにそれぞれ異なる値を指定して作成したインスタンスを使い、Encoding.GetPreambleメソッドが返す値と、Encoding.GetBytesが返す値、BinaryWriterおよびStreamWriterに書き込まれる内容を比較している。 (BinaryWriterは先頭に文字列のバイト長を書き込む点に注意)
+
&msdn(netfx,type,System.IO.BinaryWriter){BinaryWriterクラス};では、&msdn(netfx,member,System.IO.BinaryWriter.Write){Writeメソッド};で文字列を書き込むことができる。 また、このときに使用するEncodingをコンストラクタの引数&var{encoding};で指定することができる。
+

         
+
ただし、BinaryWriterはpreambleの内容に関わらず''BOMを出力しない''。 そのためBinaryWriterでBOMを出力させたい場合は、preambleを取得して明示的に書き込む必要がある。
+

         
+
なお、BinaryWriter.Writeメソッドでは文字列のバイト長を前置した上で書き込む動作となっている点に注意。
 

        

        
 
$samplecode(lang=c#,copyright-year=2016,各Encodingコンストラクタに指定する値とGetPreamble等のメソッドの動作、書き込まれる内容の違いを調べる)
$samplecode(lang=c#,copyright-year=2016,各Encodingコンストラクタに指定する値とGetPreamble等のメソッドの動作、書き込まれる内容の違いを調べる)
 
#code{{
#code{{
 
using System;
using System;
-
using System.Collections.Generic;
 
using System.IO;
using System.IO;
 
using System.Text;
using System.Text;
 

        

        
~
const string text = "日本語";
class UnicodePreamble {
-
  static void Main()
-
  {
-
    const bool bigEndian = true;
-
    const bool littleEndian = false;
-
    const bool bom = true;
-

         
-
    var unicodeEncodings = new Dictionary<string, Encoding>() {
-
      {"UTF-8",                     new UTF8Encoding(!bom)},
-
      {"UTF-8 (BOM)",               new UTF8Encoding( bom)},
-
      {"Encoding.UTF8",             Encoding.UTF8},
-
      {"UTF-16LE",                  new UnicodeEncoding(littleEndian, !bom)},
-
      {"UTF-16BE",                  new UnicodeEncoding(   bigEndian, !bom)},
-
      {"UTF-16LE (BOM)",            new UnicodeEncoding(littleEndian,  bom)},
-
      {"UTF-16BE (BOM)",            new UnicodeEncoding(   bigEndian,  bom)},
-
      {"Encoding.Unicode",          Encoding.Unicode},
-
      {"Encoding.BigEndianUnicode", Encoding.BigEndianUnicode},
-
      {"UTF-32LE",                  new UTF32Encoding(littleEndian, !bom)},
-
      {"UTF-32BE",                  new UTF32Encoding(   bigEndian, !bom)},
-
      {"UTF-32LE (BOM)",            new UTF32Encoding(littleEndian,  bom)},
-
      {"UTF-32BE (BOM)",            new UTF32Encoding(   bigEndian,  bom)},
-
      {"Encoding.UTF32",            Encoding.UTF32},
-
    };
 

        

        
~
// 比較のため文字列のバイト表現を取得して表示
    var text = "日本語";
+
Console.WriteLine("{0} ('{1}')", BitConverter.ToString(Encoding.UTF8.GetBytes(text)), text);
+
Console.WriteLine();
 

        

        
~
foreach (var createBinaryWriter in new Func<Stream, BinaryWriter>[] {
    foreach (var pair in unicodeEncodings) {
~
  // 引数encodingに指定する値を変えてBinaryWriterを作成する
      var encoding = pair.Value;
+
  s => new(s), // encodingの指定なし
+
  s => new(s, encoding: Encoding.UTF8),
+
  s => new(s, encoding: Encoding.Default), // ⚠.NET FrameworkのEncoding.DefaultはUTF-8ではなくShift_JIS等のANSIコードページとなる
+
  s => new(s, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)),
+
  s => new(s, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)),
+
}) {
+
  using var stream = new MemoryStream();
 

        

        
~
  using (var writer = createBinaryWriter(stream)) {
      Console.WriteLine(pair.Key);
~
    writer.Write(text);
      Console.WriteLine("  BOM: {0}", BitConverter.ToString(encoding.GetPreamble()));
+
  }
 

        

        
~
  // BinaryWriterが出力したバイト列を16進形式で表示
      // StreamWriter
~
  Console.WriteLine(BitConverter.ToString(stream.ToArray()));
      using (var stream = new MemoryStream()) {
~
}
        using (var writer = new StreamWriter(stream, encoding)) {
~
}}
          writer.Write(text);
~
$samplecode
        }
~
#prompt{{
        Console.WriteLine("  StreamWriter     :    {0}", BitConverter.ToString(stream.ToArray()));
~
E6-97-A5-E6-9C-AC-E8-AA-9E ('日本語')
      }
 

        

        
~
09-E6-97-A5-E6-9C-AC-E8-AA-9E
      // Encoding.GetBytes
~
09-E6-97-A5-E6-9C-AC-E8-AA-9E
      Console.WriteLine("  Encoding.GetBytes:    {0}", BitConverter.ToString(encoding.GetBytes(text)));
+
09-E6-97-A5-E6-9C-AC-E8-AA-9E
+
09-E6-97-A5-E6-9C-AC-E8-AA-9E
+
09-E6-97-A5-E6-9C-AC-E8-AA-9E
+
}}
+
$samplecode$
 

        

        
~
$relevantdocs
      // BinaryWriter
~

         
      using (var stream = new MemoryStream()) {
~
-[[programming/netfx/stream/3_binaryreader_binarywriter#BinaryWriter]]
        using (var writer = new BinaryWriter(stream, encoding)) {
~
-[[programming/netfx/stream/3_binaryreader_binarywriter#BinaryWriter_WriteString]]
          writer.Write(text);
~

         
        }
~
$relevantdocs$
        Console.WriteLine("  BinaryWriter     : {0}", BitConverter.ToString(stream.ToArray()));
~

         
      }
~

         
    }
+

         
+
**System.Xml名前空間 [#System.Xml]
+
***XmlWriter [#System.Xml.XmlWriter]
+
&msdn(netfx,member,System.Xml.dll,System.Xml.XmlWriter.Create){XmlWriter.Createメソッド};で作成したXmlWriterを使用する場合、BOMが出力されるかどうかは&msdn(netfx,member,System.Xml.dll,System.Xml.XmlWriterSettings.Encoding){XmlWriterSettings.Encodingプロパティ};に設定するEncoding次第となる。 デフォルトでは&msdn(netfx,member,System.Text.Encoding.UTF8){Encoding.UTF8};が設定されているため、''特に指定しなかった場合はBOMが出力される''。
+

         
+
したがって、BOMを出力したくない場合は、BOMを出力しないEncodingを明示的にXmlWriterSettings.Encodingに指定する必要がある。
+

         
+
$samplecode(lang=c#-9.0,copyright-year=2020,XmlWriterSettings.Encodingプロパティに指定するEncodingと、XmlWriterのBOM出力の有無の違い)
+
#code{{
+
using System;
+
using System.IO;
+
using System.Text;
+
using System.Xml;
+

         
+
const string path = "out.xml";
+

         
+
var doc = new XmlDocument();
+

         
+
doc.AppendChild(doc.CreateElement("doc"));
+

         
+
foreach (var createWriterSettings in new Func<XmlWriterSettings>[] {
+
  // Encodingプロパティに指定する値を変えてXmlWriterSettingsを作成する
+
  () => new(), // Encodingの指定なし (デフォルトではEncoding.UTF8が設定されている)
+
  () => new() { Encoding = Encoding.UTF8 },
+
  () => new() { Encoding = Encoding.Default }, // ⚠.NET FrameworkのEncoding.DefaultはUTF-8ではなくShift_JIS等のANSIコードページとなる
+
  () => new() { Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true) },
+
  () => new() { Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false) },
+
}) {
+
  using (var writer = XmlWriter.Create(path, createWriterSettings())) {
+
    doc.Save(writer);
 
  }
  }
+

         
+
  // XmlWriterが出力した内容を文字列・16進形式で表示
+
  Console.WriteLine(File.ReadAllText(path));
+
  Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(path)));
+
  Console.WriteLine();
 
}
}
 
}}
}}
 
$samplecode
$samplecode
~
#prompt(emphasize=brace){{
#prompt{{
~
<?xml version="1.0" encoding="utf-8"?><doc />
UTF-8
~
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-64-6F-63-20-2F-3E
  BOM: 
~

         
  StreamWriter     :    E6-97-A5-E6-9C-AC-E8-AA-9E
~
<?xml version="1.0" encoding="utf-8"?><doc />
  Encoding.GetBytes:    E6-97-A5-E6-9C-AC-E8-AA-9E
~
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-64-6F-63-20-2F-3E
  BinaryWriter     : 09-E6-97-A5-E6-9C-AC-E8-AA-9E
~

         
UTF-8 (BOM)
~
<?xml version="1.0" encoding="utf-8"?><doc />
  BOM: EF-BB-BF
~
3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-64-6F-63-20-2F-3E
  StreamWriter     :    EF-BB-BF-E6-97-A5-E6-9C-AC-E8-AA-9E
~

         
  Encoding.GetBytes:    E6-97-A5-E6-9C-AC-E8-AA-9E
~
<?xml version="1.0" encoding="utf-8"?><doc />
  BinaryWriter     : 09-E6-97-A5-E6-9C-AC-E8-AA-9E
~
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-64-6F-63-20-2F-3E
Encoding.UTF8
~

         
  BOM: EF-BB-BF
~
<?xml version="1.0" encoding="utf-8"?><doc />
  StreamWriter     :    EF-BB-BF-E6-97-A5-E6-9C-AC-E8-AA-9E
~
3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-64-6F-63-20-2F-3E
  Encoding.GetBytes:    E6-97-A5-E6-9C-AC-E8-AA-9E
~

         
  BinaryWriter     : 09-E6-97-A5-E6-9C-AC-E8-AA-9E
-
UTF-16LE
-
  BOM: 
-
  StreamWriter     :    E5-65-2C-67-9E-8A
-
  Encoding.GetBytes:    E5-65-2C-67-9E-8A
-
  BinaryWriter     : 06-E5-65-2C-67-9E-8A
-
UTF-16BE
-
  BOM: 
-
  StreamWriter     :    65-E5-67-2C-8A-9E
-
  Encoding.GetBytes:    65-E5-67-2C-8A-9E
-
  BinaryWriter     : 06-65-E5-67-2C-8A-9E
-
UTF-16LE (BOM)
-
  BOM: FF-FE
-
  StreamWriter     :    FF-FE-E5-65-2C-67-9E-8A
-
  Encoding.GetBytes:    E5-65-2C-67-9E-8A
-
  BinaryWriter     : 06-E5-65-2C-67-9E-8A
-
UTF-16BE (BOM)
-
  BOM: FE-FF
-
  StreamWriter     :    FE-FF-65-E5-67-2C-8A-9E
-
  Encoding.GetBytes:    65-E5-67-2C-8A-9E
-
  BinaryWriter     : 06-65-E5-67-2C-8A-9E
-
Encoding.Unicode
-
  BOM: FF-FE
-
  StreamWriter     :    FF-FE-E5-65-2C-67-9E-8A
-
  Encoding.GetBytes:    E5-65-2C-67-9E-8A
-
  BinaryWriter     : 06-E5-65-2C-67-9E-8A
-
Encoding.BigEndianUnicode
-
  BOM: FE-FF
-
  StreamWriter     :    FE-FF-65-E5-67-2C-8A-9E
-
  Encoding.GetBytes:    65-E5-67-2C-8A-9E
-
  BinaryWriter     : 06-65-E5-67-2C-8A-9E
-
UTF-32LE
-
  BOM: 
-
  StreamWriter     :    E5-65-00-00-2C-67-00-00-9E-8A-00-00
-
  Encoding.GetBytes:    E5-65-00-00-2C-67-00-00-9E-8A-00-00
-
  BinaryWriter     : 0C-E5-65-00-00-2C-67-00-00-9E-8A-00-00
-
UTF-32BE
-
  BOM: 
-
  StreamWriter     :    00-00-65-E5-00-00-67-2C-00-00-8A-9E
-
  Encoding.GetBytes:    00-00-65-E5-00-00-67-2C-00-00-8A-9E
-
  BinaryWriter     : 0C-00-00-65-E5-00-00-67-2C-00-00-8A-9E
-
UTF-32LE (BOM)
-
  BOM: FF-FE-00-00
-
  StreamWriter     :    FF-FE-00-00-E5-65-00-00-2C-67-00-00-9E-8A-00-00
-
  Encoding.GetBytes:    E5-65-00-00-2C-67-00-00-9E-8A-00-00
-
  BinaryWriter     : 0C-E5-65-00-00-2C-67-00-00-9E-8A-00-00
-
UTF-32BE (BOM)
-
  BOM: 00-00-FE-FF
-
  StreamWriter     :    00-00-FE-FF-00-00-65-E5-00-00-67-2C-00-00-8A-9E
-
  Encoding.GetBytes:    00-00-65-E5-00-00-67-2C-00-00-8A-9E
-
  BinaryWriter     : 0C-00-00-65-E5-00-00-67-2C-00-00-8A-9E
-
Encoding.UTF32
-
  BOM: FF-FE-00-00
-
  StreamWriter     :    FF-FE-00-00-E5-65-00-00-2C-67-00-00-9E-8A-00-00
-
  Encoding.GetBytes:    E5-65-00-00-2C-67-00-00-9E-8A-00-00
-
  BinaryWriter     : 0C-E5-65-00-00-2C-67-00-00-9E-8A-00-00
 
}}
}}
 
$samplecode$
$samplecode$
 

        

        
~
$relevantdocs
**File.WriteAllTextでのBOM出力 [#File.WriteAllText]
~

         
&msdn(netfx,member,System.IO.File.WriteAllText){File.WriteAllText};など、テキストをファイルに書き込むFileクラスのメソッド使った場合についてもStreamWriterと同様の動作でBOMが出力される。
+
-[[programming/netfx/xml/1_xmlwritersettings#XmlWriterSettings]]
+
-[[programming/netfx/xml/1_xmlwritersettings#XmlWriterSettings.Encoding]]
+

         
+
$relevantdocs$
 

        

        
~

         
$samplecode(lang=c#,copyright-year=2016,File.WriteAllTextメソッドに指定するEncodingとBOM出力の有無の違い)
+
***XmlDocument [#System.Xml.XmlDocument]
+
XmlWriterを使わず&msdn(netfx,member,System.Xml.ReaderWriter.dll,System.Xml.XmlDocument.Save){XmlDocument.Saveメソッド};でXML文書を出力する場合は次のようになる。
+

         
+
XML宣言(``XmlDeclaration``)のencoding属性に``"utf-8"``などを指定した場合は、''BOM付きのUTF-8''で出力される。 一方``null``や``string.Empty``を指定した場合は''BOMなしのUTF-8''で出力されるが、encoding属性は省略される。
+

         
+
したがって、BOMを出力せず、かつencoding属性付きのXML宣言を出力したい場合には[[XmlWriterを使う>#System.Xml.XmlWriter]]必要がある。
+

         
+
$samplecode(lang=c#-9.0,copyright-year=2020,XML宣言のencoding属性に指定する値と、XmlDocument.SaveメソッドのBOM出力の有無の違い)
 
#code{{
#code{{
 
using System;
using System;
 
using System.IO;
using System.IO;
~
using System.Xml;
using System.Text;
 

        

        
~
const string path = "out.xml";
class Sample {
-
  static void Main()
-
  {
-
    const string text = "日本語";
-
    const string filename = "out.txt";
 

        

        
~
foreach (var createXmlDeclaration in new Func<XmlDocument, XmlDeclaration>[] {
    // encodingを指定しない場合 (BOMは出力されない)
~
  // encoding属性に指定する値を変えてXML宣言(XmlDeclaration)を作成する
    File.WriteAllText(filename, text);
+
  d => d.CreateXmlDeclaration(version: "1.0", encoding: "utf-8", standalone: null),
+
  d => d.CreateXmlDeclaration(version: "1.0", encoding: null, standalone: null),
+
  d => d.CreateXmlDeclaration(version: "1.0", encoding: string.Empty, standalone: null),
+
}) {
+
  var doc = new XmlDocument();
 

        

        
~
  doc.AppendChild(createXmlDeclaration(doc));
    Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(filename)));
+
  doc.AppendChild(doc.CreateElement("doc"));
 

        

        
~
  doc.Save(path);
    var encodings = new Encoding[] {
-
      Encoding.UTF8,
-
      new UTF8Encoding(true),
-
      new UTF8Encoding(false),
-
    };
 

        

        
~
  // XmlDocument.Saveが出力した内容を文字列・16進形式で表示
    foreach (var encoding in encodings) {
~
  Console.WriteLine(File.ReadAllText(path));
      // encodingを指定した場合 (encoderShouldEmitUTF8Identifier次第でBOMが出力される)
~
  Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(path)));
      File.WriteAllText(filename, text, encoding);
+
  Console.WriteLine();
+
}
+
}}
+
$samplecode
+
#prompt(emphasize=brace){{
+
<?xml version="1.0" encoding="utf-8"?>
+
<doc />
+
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-0A-3C-64-6F-63-20-2F-3E
 

        

        
~
<?xml version="1.0"?>
      Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(filename)));
~
<doc />
    }
~
3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-3F-3E-0A-3C-64-6F-63-20-2F-3E
  }
+

         
+
<?xml version="1.0"?>
+
<doc />
+
3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-3F-3E-0A-3C-64-6F-63-20-2F-3E
+

         
+
}}
+
$samplecode$
+

         
+

         
+

         
+
***XDocument (System.Xml.Linq) [#System.Xml.Linq.XDocument]
+
XmlWriterを使わず&msdn(netfx,member,System.Xml.XDocument.dll,System.Xml.Linq.XDocument.Save){XDocument.Saveメソッド};でXML文書を出力する場合は次のようになる。
+

         
+
XML宣言(``XDeclaration``)のencoding属性に``"utf-8"``/``null``/``string.Empty``のいずれを指定した場合でも''BOM付きのUTF-8''で出力される。 したがって、BOMを出力したくない場合には、BOMを出力しないEncodingを使用する[[XmlWriterを使う>#System.Xml.XmlWriter]]必要がある。
+

         
+
$samplecode(lang=c#-9.0,copyright-year=2020,XML宣言のencoding属性に指定する値と、XDocument.SaveメソッドのBOM出力の有無の違い)
+
#code{{
+
using System;
+
using System.IO;
+
using System.Xml.Linq;
+

         
+
const string path = "out.xml";
+

         
+
foreach (var createXDeclaration in new Func<XDeclaration>[] {
+
  // encoding属性に指定する値を変えてXML宣言(XDeclaration)を作成する
+
  () => new(version: "1.0", encoding: "utf-8", standalone: null),
+
  () => new(version: "1.0", encoding: null, standalone: null),
+
  () => new(version: "1.0", encoding: string.Empty, standalone: null),
+
}) {
+
  var doc = new XDocument(
+
    createXDeclaration(),
+
    new XElement("doc")
+
  );
+

         
+
  doc.Save(path);
+

         
+
  // XDocument.Saveが出力した内容を文字列・16進形式で表示
+
  Console.WriteLine(File.ReadAllText(path));
+
  Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(path)));
+
  Console.WriteLine();
 
}
}
+
}}
+
$samplecode
+
#prompt(emphasize=brace){{
+
<?xml version="1.0" encoding="utf-8"?>
+
<doc />
+
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-0A-3C-64-6F-63-20-2F-3E
+

         
+
<?xml version="1.0" encoding="utf-8"?>
+
<doc />
+
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-0A-3C-64-6F-63-20-2F-3E
+

         
+
<?xml version="1.0" encoding="utf-8"?>
+
<doc />
+
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-0A-3C-64-6F-63-20-2F-3E
+

         
 
}}
}}
 
$samplecode$
$samplecode$
 

        

        
+
$relevantdocs
+

         
+
-[[programming/netfx/xml/0_generate_write#XDocument.Save]]
+
-[[programming/netfx/xml/0_generate_write#XDeclaration]]
+

         
+
$relevantdocs$
+

         
+

         
+
**JsonSerializer (System.Text.Json) [#System.Text.Json.JsonSerializer]
+
&urn2url(urn:ietf:rfc:8259,#8.1); (8.1. Character Encoding)にて、JSONフォーマットでは(閉じたシステム内で使用する場合を除き)UTF-8を使用しなければならない(''MUST'')、ネットワーク転送される場合はBOMを付加してはならない(''MUST NOT'')とされている。
+

         
+
&msdn(netfx,type,System.Text.Json.dll,System.Text.Json.JsonSerializer){JsonSerializerクラス};はこれに沿った動作となっていて、常に''BOMなしのUTF-8''で出力される。 オプション等でEncodingを明示的に指定することはできない。
+

         
+
$samplecode(lang=c#-9.0,copyright-year=2020,JsonSerializerではBOMは出力されない)
+
#code{{
+
using System;
+
using System.IO;
+
using System.Text;
+
using System.Text.Json;
+
using System.Threading.Tasks;
+

         
+
var stream = new MemoryStream();
+

         
+
await JsonSerializer.SerializeAsync<string[]>(stream, new[] {"json"});
+

         
+
// JsonSerializerが出力した内容を文字列・16進形式で表示
+
Console.WriteLine(Encoding.UTF8.GetString(stream.ToArray()));
+
Console.WriteLine(BitConverter.ToString(stream.ToArray()));
+
}}
+
$samplecode
 
#prompt(実行結果,emphasize=brace){{
#prompt(実行結果,emphasize=brace){{
~
["json"]
E6-97-A5-E6-9C-AC-E8-AA-9E
~
5B-22-6A-73-6F-6E-22-5D
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
-
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
-
E6-97-A5-E6-9C-AC-E8-AA-9E
 
}}
}}
+
$samplecode$
 

        

        
~
ファイルとして出力する際など、なんらかの理由でBOMを付加する必要がある場合は、次のように出力ストリームにBOMを書き込んでからJsonSerializerで書き込むことにより、BOMありのUTF-8として出力できる。
*XML関連クラスでのBOM出力
-
**XmlWriter [#System.Xml.XmlWriter]
-
&msdn(netfx,member,System.Xml.dll,System.Xml.XmlWriter.Create){XmlWriter.Createメソッド};で作成したXmlWriterを使用する場合、BOMが出力されるかどうかは&msdn(netfx,member,System.Xml.dll,System.Xml.XmlWriterSettings.Encoding){XmlWriterSettings.Encodingプロパティ};に設定するEncoding次第となる。
 

        

        
~
$samplecode(lang=c#-9.0,copyright-year=2020,JsonSerializerを使ってBOM付きでファイルに出力する)
ただし、デフォルトではXmlWriterSettings.Encodingに&msdn(netfx,member,System.Text.Encoding.UTF8){Encoding.UTF8};が設定されているため、''特に指定しなかった場合はBOMが出力される''。 従って、BOMを出力したくない場合には、BOMを出力しないEncodingをXmlWriterSettings.Encodingに指定する必要がある。
+
#code{{
+
using System;
+
using System.IO;
+
using System.Text;
+
using System.Text.Json;
+
using System.Threading.Tasks;
+

         
+
const string path = "out.json";
+

         
+
using (var stream = File.OpenWrite(path)) {
+
  stream.SetLength(0L); // 既存の内容を上書きする(truncate)
+

         
+
  stream.Write(Encoding.UTF8.Preamble); // UTF-8のBOMを書き込む
+

         
+
  await JsonSerializer.SerializeAsync<string[]>(stream, new[] {"json"}); // JsonSerializerでStreamに書き込む
+
}
+

         
+
// JsonSerializerが出力した内容を文字列・16進形式で表示
+
Console.WriteLine(File.ReadAllText(path));
+
Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(path)));
+
}}
+
$samplecode
+
#prompt(emphasize=brace){{
+
["json"]
+
{{EF-BB-BF}}-5B-22-6A-73-6F-6E-22-5D
+
}}
+
$samplecode$
 

        

        
~

         
$samplecode(lang=c#,copyright-year=2016,XmlWriterSettings.Encodingプロパティに指定するEncodingと、XmlWriterのBOM出力の有無の違い)
+

         
+

         
+
*BOMによる自動判別とBOMの透過的読み込みを行うStream [#BomTransparentStream]
+
以下は、BOMからEncodingを自動判別し、BOMの''透過的な''読み込みを行うStreamラッパー``BomTransparentStream``を実装した例。
+

         
+
与えられたStreamからBOMを読み込み、Encodingの自動判別を行うと同時に、コンストラクタでの指定に応じてBOMを維持/破棄した上で読み込むことができる。 StreamReaderとは異なり、自動判別できなかった場合は''BOMなしのUTF-8''として判別される。
+

         
+
簡易な実装かつ限定的なユースケースのみでの動作確認しかしていないため、使用方法によっては実装を修正する必要が出てくると思われる。 その他の制限・未実装事項等についてはコード中のコメントを参照。
+

         
+
$samplecode(lang=c#-9.0,copyright-year=2020,BomTransparentStream)
 
#code{{
#code{{
 
using System;
using System;
 
using System.IO;
using System.IO;
+
using System.Linq;
 
using System.Text;
using System.Text;
-
using System.Xml;
 

        

        
~
/// <summary>与えられたStreamの内容を読み込み、BOMを維持/破棄した上で透過的に返すStream。 同時にBOMからEncodingを自動判別も行う。</summary>
class Sample {
~
class BomTransparentStream : Stream {
  static void Main()
+
  private Stream baseStream;
+
  public Stream BaseStream => baseStream ?? throw new ObjectDisposedException(GetType().FullName);
+

         
+
  private readonly bool discardBom;
+

         
+
  public BomTransparentStream(string path, bool discardBom = true)
+
    : this(File.OpenRead(path), discardBom) {}
+

         
+
  public BomTransparentStream(Stream baseStream, bool discardBom = true)
+
  {
+
    this.baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
+
    this.discardBom = discardBom;
+
  }
+

         
+
  public override void Close()
 
  {
  {
~
    baseStream?.Close();
    const string filename = "out.xml";
+
    baseStream = null;
 

        

        
~
    base.Close();
    var doc = new XmlDocument();
+
  }
 

        

        
~
  public override bool CanSeek => false; // シークはサポートしない
    doc.AppendChild(doc.CreateElement("要素"));
+
  public override bool CanRead => BaseStream.CanRead;
+
  public override bool CanWrite => false; // 書き込みはサポートしない
+
  public override bool CanTimeout => BaseStream.CanTimeout;
+
  public override long Position {
+
    get => BaseStream.Position; // 本来ならBOM分を差し引いた値を返すべき
+
    set => throw new NotSupportedException();
+
  }
+
  public override long Length => BaseStream.Length; // 本来ならBOM分を差し引いた値を返すべき
+
  public override void SetLength(long @value) => throw new NotSupportedException(); // 長さの設定はサポートしない
+
  public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
+
  public override void Flush() => throw new NotSupportedException();
+
  public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
+

         
+
  /// <summary>BOMから自動判別されたEncoding</summary>
+
  /// <remarks>未判別の状態、また判別できなかった場合はEncoding.Defaultを返す</remarks>
+
  public Encoding DetectedEncoding { get; private set; } = Encoding.Default;
+

         
+
  /// <summary>まだBOMを読み込んでいない場合は、BOMの読み込みとEncodingの自動判別を行う</summary>
+
  public void ReadPreamble()
+
  {
+
    if (isPreamble)
+
      Read(Array.Empty<byte>(), 0, 0);
+
  }
 

        

        
~
  private bool isPreamble = true;
    var encodings = new Encoding[] {
~
  private ReadOnlyMemory<byte> preamble = default;
      null,
-
      Encoding.UTF8,
-
      new UTF8Encoding(true),
-
      new UTF8Encoding(false),
-
    };
 

        

        
~
  /// <summary>BaseStreamから読み込み、BOMを維持/破棄した結果を返す。 最初に呼び出された際は、BOMからEncodingの自動判別も行う。</summary>
    foreach (var encoding in encodings) {
~
  public override int Read(byte[] buffer, int offset, int count)
      var settings = new XmlWriterSettings();
+
  {
+
    // 引数チェックは省略
 

        

        
~
    if (isPreamble) {
      // XmlWriterSettings.Encodingの設定を変える
~
      isPreamble = false;
      // (nullの場合はデフォルトの設定をそのまま使用する)
-
      if (encoding != null)
-
        settings.Encoding = encoding;
 

        

        
~
      // まだBOMを読み込んでいない場合は、BaseStreamの先頭4バイト分を読み込む
      using (var writer = XmlWriter.Create(filename, settings)) {
~
      Memory<byte> _preamble = new byte[4];
        doc.Save(writer);
~

         
      }
+
      BaseStream.Read(_preamble.Span); // 戻り値のチェックは省略、この実装ではStreamの内容が4バイト未満の場合は想定していない
+

         
+
      // Encodingの判別を行い、判別できた場合はBOMを除去した結果を取得する
+
      var (detectedEncoding, remainder) = DetectEncodingFromPreamble(_preamble);
+

         
+
      DetectedEncoding = detectedEncoding;
 

        

        
~
      // BOMを維持/破棄した内容をpreambleとして保持する
      Console.WriteLine(File.ReadAllText(filename));
~
      preamble = discardBom ? remainder : _preamble;
      Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(filename)));
-
      Console.WriteLine();
 
    }
    }
-
  }
-
}
-
}}
-
$samplecode$
 

        

        
~
    var buf = buffer.AsMemory(offset, count);
#prompt(実行結果,emphasize=brace){{
~
    var readCount = 0;
<?xml version="1.0" encoding="utf-8"?><要素 />
~

         
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-E8-A6-81-E7-B4-A0-20-2F-3E
+
    for (;;) {
+
      // preambleとして保持されている内容が空なら、BaseStreamから読み込み、そのまま返す
+
      if (preamble.IsEmpty)
+
        return readCount + BaseStream.Read(buf.Span);
+

         
+
      // preambleの内容をbufferにコピーする
+
      if (preamble.Length <= buf.Length) {
+
        var len = preamble.Length;
 

        

        
~
        preamble.CopyTo(buf);
<?xml version="1.0" encoding="utf-8"?><要素 />
~
        preamble = default; // コピー済みのため、空にする
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-E8-A6-81-E7-B4-A0-20-2F-3E
 

        

        
~
        buf = buf.Slice(len);
<?xml version="1.0" encoding="utf-8"?><要素 />
-
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-E8-A6-81-E7-B4-A0-20-2F-3E
 

        

        
~
        readCount += len;
<?xml version="1.0" encoding="utf-8"?><要素 />
~
      }
3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-E8-A6-81-E7-B4-A0-20-2F-3E
+
      else {
+
        var len = buf.Length;
+

         
+
        preamble.Slice(0, len).CopyTo(buf);
+
        preamble = preamble.Slice(len);
+

         
+
        return len;
+
      }
+
    }
+
  }
 

        

        
+
  private static (Encoding, ReadOnlyMemory<byte>) DetectEncodingFromPreamble(ReadOnlyMemory<byte> preamble)
+
  {
+
    // 与えられたpreambleからEncodingを判別する
+
    var e = encodings.FirstOrDefault(e => preamble.Span.StartsWith(e.Preamble)) ?? Encoding.Default;
+

         
+
    // 判別したEncodingと、BOMを差し引いた分を返す
+
    return (e, preamble.Slice(e.Preamble.Length));
+
  }
+

         
+
  // BOMの長い順にソート済みのEncoding
+
  private static readonly Encoding[] encodings = new[] {
+
    Encoding.UTF32,
+
    new UTF32Encoding(bigEndian: true, byteOrderMark: true),
+
    Encoding.UTF8,
+
    Encoding.Unicode,
+
    Encoding.BigEndianUnicode,
+
  };
+
}
 
}}
}}
+
$samplecode$
+

         
 

        

        
-
$remarks
-
XmlWriterSettingsによるXmlWriterの動作制御については[[programming/netfx/xml/1_xmlwritersettings]]を参照のこと。
-
$remarks$
 

        

        
~
以下は使用例。
**XmlDocument (System.Xml) [#System.Xml.XmlDocument]
-
XmlWriterを使わずXmlDocument.SaveメソッドでXML文書を出力する場合は次のようになる。 XML宣言(XmlDeclaration)のencoding属性に``"utf-8"``などを指定した場合は、''BOM付きのUTF-8''で出力される。 一方``null``や``string.Empty``を指定した場合は''BOMなしのUTF-8''で出力されるが、encoding属性は省略される。 従って、BOMを出力せず、かつencoding属性付きのXML宣言を出力したい場合にはXmlWriterを使う必要がある。
 

        

        
~
$samplecode(lang=c#,copyright-year=2020,使用例1: ファイルを読み込み、BOMを除去した上で上書きする)
$samplecode(lang=c#,copyright-year=2016,XML宣言のencoding属性に指定する値と、XmlDocument.SaveメソッドのBOM出力の有無の違い)
 
#code{{
#code{{
 
using System;
using System;
 
using System.IO;
using System.IO;
~
using System.Text;
using System.Xml;
 

        

        
 
class Sample {
class Sample {
 
  static void Main()
  static void Main()
 
  {
  {
~
    var encodings = new Encoding[] {
    const string filename = "out.xml";
~
      new UTF8Encoding(encoderShouldEmitUTF8Identifier: true),

         
~
      new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
    var encodings = new string[] {
~
      new UnicodeEncoding(bigEndian: true, byteOrderMark: true),
      "utf-8",
~
      new UnicodeEncoding(bigEndian: true, byteOrderMark: false),
      null,
~
      new UnicodeEncoding(bigEndian: false, byteOrderMark: true),
      string.Empty,
+
      new UnicodeEncoding(bigEndian: false, byteOrderMark: false),
+
      new UTF32Encoding(bigEndian: true, byteOrderMark: true),
+
      new UTF32Encoding(bigEndian: true, byteOrderMark: false),
+
      new UTF32Encoding(bigEndian: false, byteOrderMark: true),
+
      new UTF32Encoding(bigEndian: false, byteOrderMark: false),
+
      shift_jis,
 
    };
    };
 

        

        
+
    const string contents = "日本語";
+
    const string path = "out.txt";
+

         
 
    foreach (var encoding in encodings) {
    foreach (var encoding in encodings) {
~
      // 各種Encoding+BOMあり/なしでファイルに書き込む
      var doc = new XmlDocument();
+
      File.WriteAllText(path, contents, encoding);
 

        

        
~
      // 出力したファイル内容を16進形式で表示
      // encoding属性に指定する値を変えてXML宣言を作成・追加する
~
      Console.Write("{0,-60} => ", BitConverter.ToString(File.ReadAllBytes(path)));
      doc.AppendChild(doc.CreateXmlDeclaration("1.0", encoding, null));
 

        

        
~
      using (var buffer = new MemoryStream()) {
      doc.AppendChild(doc.CreateElement("要素"));
+
        // 出力したファイルをBomTransparentStreamで読み込む
+
        using (var bomTransparentStream = new BomTransparentStream(path, discardBom: true /*BOMは除去する*/)) {
+
          bomTransparentStream.CopyTo(buffer); // 内容をbufferにコピー
+
        }
 

        

        
~
        using (var fileStream = File.OpenWrite(path)) {
      doc.Save(filename);
+
          fileStream.SetLength(0L); // truncate
 

        

        
~
          buffer.Position = 0L;
      Console.WriteLine(File.ReadAllText(filename));
~
          buffer.CopyTo(fileStream); // bufferの内容をコピー(ファイルに上書き)
      Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(filename)));
~
        }
      Console.WriteLine();
+
      }
+

         
+
      // 出力したファイル内容を16進形式で表示
+
      Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(path)));
 
    }
    }
 
  }
  }
+

         
+
  static readonly Encoding shift_jis =
+
#if NETFRAMEWORK
+
    Encoding.GetEncoding("shift_jis");
+
#else
+
    // `dotnet add sample.csproj package System.Text.Encoding.CodePages`
+
    CodePagesEncodingProvider.Instance.GetEncoding("shift_jis");
+
#endif
 
}
}
 
}}
}}
+
$samplecode
+
#prompt(emphasize=brace){{
+
{{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E                          => E6-97-A5-E6-9C-AC-E8-AA-9E
+
E6-97-A5-E6-9C-AC-E8-AA-9E                                   => E6-97-A5-E6-9C-AC-E8-AA-9E
+
{{FE-FF}}-65-E5-67-2C-8A-9E                                      => 65-E5-67-2C-8A-9E
+
65-E5-67-2C-8A-9E                                            => 65-E5-67-2C-8A-9E
+
{{FF-FE}}-E5-65-2C-67-9E-8A                                      => E5-65-2C-67-9E-8A
+
E5-65-2C-67-9E-8A                                            => E5-65-2C-67-9E-8A
+
{{00-00-FE-FF}}-00-00-65-E5-00-00-67-2C-00-00-8A-9E              => 00-00-65-E5-00-00-67-2C-00-00-8A-9E
+
00-00-65-E5-00-00-67-2C-00-00-8A-9E                          => 00-00-65-E5-00-00-67-2C-00-00-8A-9E
+
{{FF-FE-00-00}}-E5-65-00-00-2C-67-00-00-9E-8A-00-00              => E5-65-00-00-2C-67-00-00-9E-8A-00-00
+
E5-65-00-00-2C-67-00-00-9E-8A-00-00                          => E5-65-00-00-2C-67-00-00-9E-8A-00-00
+
93-FA-96-7B-8C-EA                                            => 93-FA-96-7B-8C-EA
+
}}
 
$samplecode$
$samplecode$
 

        

        
-
#prompt(実行結果,emphasize=brace){{
-
<?xml version="1.0" encoding="utf-8"?>
-
<要素 />
-
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-0A-3C-E8-A6-81-E7-B4-A0-20-2F-3E
-

         
-
<?xml version="1.0"?>
-
<要素 />
-
3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-3F-3E-0A-3C-E8-A6-81-E7-B4-A0-20-2F-3E
 

        

        
-
<?xml version="1.0"?>
-
<要素 />
-
3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-3F-3E-0A-3C-E8-A6-81-E7-B4-A0-20-2F-3E
 

        

        
-
}}
 

        

        
-
**XDocument (System.Xml.Linq) [#System.Xml.Linq.XDocument]
-
XmlWriterを使わずXDocument.SaveメソッドでXML文書を出力する場合、XML宣言のencoding属性に``"utf-8"``などを指定した場合と``null``や``string.Empty``を指定した場合のどちらでも''BOM付きのUTF-8''で出力される。 従って、BOMを出力したくない場合には、BOMを出力しないEncodingを使用するXmlWriterを使う必要がある。
 

        

        
~
$samplecode(lang=c#,copyright-year=2020,使用例2: ファイルを読み込み、BOMからEncodingの自動判別を行い、BOMを維持したままBinaryReaderで読み込む)
$samplecode(lang=c#,copyright-year=2016,XML宣言のencoding属性に指定する値と、XDocument.SaveメソッドのBOM出力の有無の違い)
 
#code{{
#code{{
 
using System;
using System;
 
using System.IO;
using System.IO;
~
using System.Text;
using System.Xml.Linq;
 

        

        
 
class Sample {
class Sample {
 
  static void Main()
  static void Main()
 
  {
  {
~
    var encodings = new Encoding[] {
    const string filename = "out.xml";
~
      new UTF8Encoding(encoderShouldEmitUTF8Identifier: true),

         
~
      new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
    var encodings = new string[] {
~
      new UnicodeEncoding(bigEndian: true, byteOrderMark: true),
      "utf-8",
~
      new UnicodeEncoding(bigEndian: true, byteOrderMark: false),
      null,
~
      new UnicodeEncoding(bigEndian: false, byteOrderMark: true),
      string.Empty,
+
      new UnicodeEncoding(bigEndian: false, byteOrderMark: false),
+
      new UTF32Encoding(bigEndian: true, byteOrderMark: true),
+
      new UTF32Encoding(bigEndian: true, byteOrderMark: false),
+
      new UTF32Encoding(bigEndian: false, byteOrderMark: true),
+
      new UTF32Encoding(bigEndian: false, byteOrderMark: false),
+
      shift_jis,
 
    };
    };
 

        

        
+
    const string contents = "日本語";
+
    const string path = "out.txt";
+

         
 
    foreach (var encoding in encodings) {
    foreach (var encoding in encodings) {
~
      // 各種Encoding+BOMあり/なしでファイルに書き込む
      var doc = new XDocument(
~
      File.WriteAllText(path, contents, encoding);
        new XDeclaration("1.0", encoding, null), // encoding属性に指定する値を変えてXML宣言を作成・追加する
-
        new XElement("要素")
-
      );
 

        

        
~
      using (var bomTransparentStream = new BomTransparentStream(path, discardBom: false /*BOMは維持する*/)) {
      doc.Save(filename);
+
        bomTransparentStream.ReadPreamble(); // Streamの先頭4バイトを読み込み、Encodingの自動判別を行う
 

        

        
~
        var e = bomTransparentStream.DetectedEncoding; // 自動判別されたEncodingを取得する
      Console.WriteLine(File.ReadAllText(filename));
~

         
      Console.WriteLine(BitConverter.ToString(File.ReadAllBytes(filename)));
~
        Console.Write($"{e.WebName,12} {(e.Preamble.Length == 0 ? "   " : "BOM")} : ");
      Console.WriteLine();
+

         
+
        // BinaryReaderでStreamの内容を読み込む
+
        var reader = new BinaryReader(bomTransparentStream);
+

         
+
        Console.WriteLine(BitConverter.ToString(reader.ReadBytes((int)bomTransparentStream.Length)));
+
      }
 
    }
    }
 
  }
  }
+

         
+
  static readonly Encoding shift_jis =
+
#if NETFRAMEWORK
+
    Encoding.GetEncoding("shift_jis");
+
#else
+
    // `dotnet add sample.csproj package System.Text.Encoding.CodePages`
+
    CodePagesEncodingProvider.Instance.GetEncoding("shift_jis");
+
#endif
 
}
}
 
}}
}}
+
$samplecode
+
#prompt(emphasize=brace){{
+
       utf-8 BOM : {{EF-BB-BF}}-E6-97-A5-E6-9C-AC-E8-AA-9E
+
       utf-8     : E6-97-A5-E6-9C-AC-E8-AA-9E
+
    utf-16BE BOM : {{FE-FF}}-65-E5-67-2C-8A-9E
+
       utf-8     : 65-E5-67-2C-8A-9E
+
      utf-16 BOM : {{FF-FE}}-E5-65-2C-67-9E-8A
+
       utf-8     : E5-65-2C-67-9E-8A
+
    utf-32BE BOM : {{00-00-FE-FF}}-00-00-65-E5-00-00-67-2C-00-00-8A-9E
+
       utf-8     : 00-00-65-E5-00-00-67-2C-00-00-8A-9E
+
      utf-32 BOM : {{FF-FE-00-00}}-E5-65-00-00-2C-67-00-00-9E-8A-00-00
+
       utf-8     : E5-65-00-00-2C-67-00-00-9E-8A-00-00
+
       utf-8     : 93-FA-96-7B-8C-EA
+
}}
 
$samplecode$
$samplecode$
 

        

        
-
#prompt(実行結果,emphasize=brace){{
-
<?xml version="1.0" encoding="utf-8"?>
-
<要素 />
-
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-0D-0A-3C-E8-A6-81-E7-B4-A0-20-2F-3E
 

        

        
-
<?xml version="1.0" encoding="utf-8"?>
-
<要素 />
-
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-0D-0A-3C-E8-A6-81-E7-B4-A0-20-2F-3E
 

        

        
-
<?xml version="1.0" encoding="utf-8"?>
-
<要素 />
-
{{EF-BB-BF}}-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-0D-0A-3C-E8-A6-81-E7-B4-A0-20-2F-3E
 

        

        
~
$samplecode(lang=c#,copyright-year=2020,先にEncodingの自動判別を行い、判別されたEncodingを使ってStreamReaderで読み込む)
}}
+
#code{{
+
using System;
+
using System.IO;
+
using System.Text;
 

        

        
~
class Sample {
$remarks
~
  static void Main()
XDocumentにおけるXML宣言については[[programming/netfx/xml/0_generate_write#XDeclaration]]、Saveメソッドによる保存については[[programming/netfx/xml/0_generate_write#XDocument.Save]]を参照のこと。
~
  {
$remarks$
+
    var encodings = new Encoding[] {
+
      new UTF8Encoding(encoderShouldEmitUTF8Identifier: true),
+
      new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
+
      new UnicodeEncoding(bigEndian: true, byteOrderMark: true),
+
      new UnicodeEncoding(bigEndian: true, byteOrderMark: false),
+
      new UnicodeEncoding(bigEndian: false, byteOrderMark: true),
+
      new UnicodeEncoding(bigEndian: false, byteOrderMark: false),
+
      new UTF32Encoding(bigEndian: true, byteOrderMark: true),
+
      new UTF32Encoding(bigEndian: true, byteOrderMark: false),
+
      new UTF32Encoding(bigEndian: false, byteOrderMark: true),
+
      new UTF32Encoding(bigEndian: false, byteOrderMark: false),
+
      shift_jis,
+
    };
+
    const string text = "日本語";
+

         
+
    foreach (var encoding in encodings) {
+
      // 各種Encoding+BOMあり/なしでMemoryStreamに書き込む
+
      Console.Write($"{encoding.WebName,12} {(encoding.Preamble.Length == 0 ? "   " : "BOM")} => ");
+

         
+
      using var stream = new MemoryStream();
+

         
+
      using (var writer = new StreamWriter(stream, encoding, leaveOpen: true)) {
+
        writer.Write(text);
+
      }
+

         
+
      stream.Position = 0L;
+

         
+
      // BOMによるEncodingの自動判別を有効にしてMemoryStreamから読み込む
+
      using (var bomTransparentStream = new BomTransparentStream(stream, discardBom: true)) {
+
        // Streamの先頭4バイトを読み込み、Encodingの自動判別を行う
+
        bomTransparentStream.ReadPreamble();
+

         
+
        // 自動判別されたEncodingを参照する
+
        var e = bomTransparentStream.DetectedEncoding;
+

         
+
        Console.Write($"{e.WebName,12} {(e.Preamble.Length == 0 ? "   " : "BOM")} : ");
+

         
+
        // 自動判別されたEncodingに基づいてStreamReaderを作成、内容を読み込む
+
        using (var reader = new StreamReader(bomTransparentStream, encoding: bomTransparentStream.DetectedEncoding)) {
+
          Console.WriteLine(reader.ReadToEnd());
+
        }
+
      }
+
    }
+
  }
+

         
+
  static readonly Encoding shift_jis =
+
#if NETFRAMEWORK
+
    Encoding.GetEncoding("shift_jis");
+
#else
+
    // `dotnet add sample.csproj package System.Text.Encoding.CodePages`
+
    CodePagesEncodingProvider.Instance.GetEncoding("shift_jis");
+
#endif
+
}
+
}}
+
$samplecode
+
#prompt{{
+
       utf-8 BOM =>        utf-8 BOM : 日本語
+
       utf-8     =>        utf-8     : 日本語
+
    utf-16BE BOM =>     utf-16BE BOM : 日本語
+
    utf-16BE     =>        utf-8     : e�g,��
+
      utf-16 BOM =>       utf-16 BOM : 日本語
+
      utf-16     =>        utf-8     : �e,g��
+
    utf-32BE BOM =>     utf-32BE BOM : 日本語
+
    utf-32BE     =>        utf-8     : e�g,��
+
      utf-32 BOM =>       utf-32 BOM : 日本語
+
      utf-32     =>        utf-8     : �e,g��
+
   shift_jis     =>        utf-8     : ���{�
+
}}
+
$samplecode$