WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

BOM 無し UTF-8 と StreamReader

by WebSurfer 2020年5月20日 14:36

Windows 10 のあるバージョンから、メモ帳でテキストを保存する際の文字コードのデフォルトが ANSI から UTF-8 に変わりました。下の画像を見てください。

メモ帳の文字コード

上の画像で、[ANSI] は「BOM なし Shift_JIS」、[UTF-8] は「BOM なし UTF-8」となります。それ以外はすべて BOM つきとなります。

その変更で気になっていたのは、以前のメモ帳のデフォルト [ANSI] 即ち「BOM なし Shift_JIS」を期待して StreamReader コンストラクタの Encoding を Shift_JIS に指定した以下のようなコードを使っている場合、新しいメモ帳のデフォルト「BOM なし UTF-8」で保存されたファイルを読むとどうなるかということです。

using (StreamReader reader = new StreamReader(
                             dir + "UTF8withoutBOM.txt", 
                             Encoding.GetEncoding("Shift_JIS")))
{
    Console.WriteLine(reader.ReadToEnd());
}

ダメだろうとは思いながらも、念のため試してみましたがやっぱり当たり前にダメでした。下の画像の一行目がそれで、文字化けしています。

結果

上の画像の二行目以降は、順に、メモ帳で保存するとき [UTF-8 (BOM つき)]、[ANSI]、[UTF-16 BE]、[UTF-16 LE] を選んだものです。

[UTF-8] は「BOM なし UTF-8」で、BOM が付いてないので StreamReader コンストラクタの第 2 引数に指定された Shift_JIS としてデコードされ、その結果文字化けしています。

[ANSI] は「BOM なし Shift_JIS」で、これも BOM なしですが、StreamReader コンストラクタの第 2 引数で正しく指定されている通り Shift_JIS としてデコードするので問題なしです。

[UTF-8] と [ANSI] 以外は有効な BOM が付いており、StreamReader は BOM で正しく文字コードが判定できますので、判定した通りデコードされて問題なしという結果になっています。

StreamReader コンストラクターの詳しい説明は左のリンクをクリックして Microsoft のドキュメントを見てください。

少し説明しておくと、StreamReader コンストラクタ (String, Encoding) のように Encoding を引数に指定するオーバーロードでは、ファイルの最初の 4 バイトを見てエンコーディングの検出を試み、適切な BOM で始まる場合、それによって UTF-8、リトルエンディアン Unicode、ビッグエンディアン Unicode、リトルエンディアン UTF-32、およびビッグエンディアン UTF-32 を自動的に判別し、それ以外の場合は Encoding に指定されたエンコードが使用されるそうです。

ちなみに BOM は 16 進数表現で以下の通りとなります。

  • UTF-8:    EF BB BF
  • UTF-16BE: FE FF
  • UTF-16LE: FF FE
  • UTF-32BE: 00 00 FE FF
  • UTF-32LE: FF FE 00 00

メモ帳のデフォルトが「BOM なし Shift_JIS」だった時代は、メモ帳で作るテキストファイルを読む場合、StreamReader コンストラクタの Encoding に Shift_JIS 指定を指定しておけば文字化けに悩まずに済みました。デフォルトが「BOM なし UTF-8」に変わった今はそうはいかないようです。

Tags: , ,

.NET Framework

応答ヘッダが 64KB を超えるとエラー

by WebSurfer 2019年9月2日 12:00

HttpClient を使った HPPT 通信で、応答ヘッダが 64KB を超えると WebException 例外がスローされるという話を備忘録として書いておきます。

(元の話は Teratail のスレッド「GetAsync処理時のメッセージの長さが制限の解消方法について」のもので、実際に自分が経験した訳ではなく聞いた話です)

同様な問題は HttpWebResponse / HttpWebRequest を使った時から起こっていた問題だそうで、応答ヘッダが 64KB を超えると WebException がスローされ、以下のエラーメッセージが出るそうです。

"接続が切断されました: メッセージの長さが制限を超えています。"

英文では、

"The underlying connection was closed: The message length limit was exceeded."

応答ヘッダが 64KB を超えるというのはレアなのか、日本語のエラーメッセージでググっても参考になる記事はヒットしませんでした。

でも、英語圏まで検索範囲を広げる(英文でググる)と HttpWebResponse / HttpWebRequest でこの問題に遭遇した人はいるようで、WebException: "The message length limit was exceeded" 他の記事がヒットします。

その記事に書いてある解決策は、HttpWebRequest の MaximumResponseHeadersLength プロパティを -1 (無制限) に設定することだそうです。(未検証・未確認です)

HttpClient を使う場合は、.NET Framework 4.7.1 以降ですが、HttpClientHandler クラスMaxResponseHeadersLength プロパティを使って応答ヘッダのサイズの許容最大値を設定できるそうです。

// Create an HttpClientHandler object
HttpClientHandler handler = new HttpClientHandler();
handler.MaxResponseHeadersLength = 128;  // 128KB

// Create an HttpClient object
HttpClient client = new HttpClient(handler);

.NET 4.5 では MaxResponseHeadersLength プロパティは使えず .NET 4.7.1 で使えるようになったということは、HttpWebRequest / HttpWebResponse で起こっていた問題に対応できないことを指摘されて追加したのかもしれませんね。(想像です)

Tags: , , ,

.NET Framework

構成ファイルの保存場所

by WebSurfer 2019年9月1日 15:33

Windows Forms、WPF、Console アプリの構成ファイルの保存場所はどこかという話を書きます。(元の話は Teratail のスレッド「app.configの情報を書き換えたい」です)

Settings

Visual Studio でアプリケーション開発の際、自動的に App.config というファイルが生成され、それに接続文字列などの設定値が保存されますが、アプリを実行するときに使われる構成ファイルは App.config とは別に生成され、別の場所に保存されます。

まず、アプリをビルドする際、生成される .exe ファイルと同じ場所に、App.config の内容をそのままコピーして <アプリケーション名>.exe.config という名前のファイルが作られます。ただし、この他に構成ファイルが作られるケースがあります。

上の画像は Windows Forms アプリケーションの Settings の内容で、その中の MainWindows_Left, _Top, _Width, _Height はウィンドウのサイズです。ユーザーがウィンドウのサイズを変更してアプリを閉じると変更後の値を保存して、次にアプリを立ち上げたときは保存した値でウィンドウのサイズを設定するようにコーディングしています。

MainWindows_Left, _Top, _Width, _Height のデフォルト値(初期値)は App.config や <アプリケーション名>.exe.config に保存されますが、変更後の値はどこに保存されるでしょう?

ウィンドウのサイズを変更してアプリを閉じても、App.config と <アプリケーション名>.exe.config の MainWindows_Left, _Top, _Width, _Height の値は変わりません。

でも、次にアプリを立ち上げるとサイズは期待通り変更後のサイズになるので、どこかに変更後の MainWindows_Left, _Top, _Width, _Height の値が保存されているはずです。

実は、(1) すべてのユーザーに適用するグローバル構成、(2) ローミング ユーザーに適用する構成、(3) 個々のユーザーに適用する個別構成によって格納場所が違うそうです。

App.config と <アプリケーション名>.exe.config は「(1) すべてのユーザーに適用するグローバル構成」に該当するようです。

変更後の MainWindows_Left, _Top, _Width, _Height の値は「(3) 個々のユーザーに適用する個別構成」に該当するようで、以下のフォルダ下に user.config という名前で保存されています。

C:\Users\<ユーザー名>\AppData\Local\<アプリケーション名>

以下の画像はエクスプローラーで user.config の場所を表示し、その内容をメモ帳で開いて表示したものです。変更後の MainWindows_Left, _Top, _Width, _Height の値が反映されています。

user.config

ちなみに、ファイルパスはプログラムで取得することができます。

ConfigurationManager.OpenExeConfiguration メソッドConfiguration オブジェクトを取得し、その FilePath プロパティを使って取得します。

OpenExeConfiguration メソッドは引数に ConfigurationUserLevel を取るオーバーロードを使用し、ConfigurationUserLevel のフィールドを None, PerUserRoaming, PerUserRoamingAndLocal とすることで、それぞれ上の (1), (2), (3) の Configuration オブジェクトを取得できます。

Tags: ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar