プロジェクト内のテキストファイルを読み込む方法 ~埋め込みリソース~[引越記事]
はじめに
(注:Assembly
クラスはSystem.Reflection名前空間です)
アプリケーションを作成するときに、何らかのテキストファイルをアプリケーション内に持たせておいて、内部での処理や表示のために使いたいことがあります。 正しい方法かどうかは置いといて、一つのやり方としてはプロジェクトのプロパティからリソースとして追加して、
コードから以下のようにProperties.Resources.リソース名
と記述してファイルの中身を読み込むことができます。
var textFileContent = Properties.Resources.ThisIsTextFile;
実行すると、こんな感じです。
今回はテキストファイルですけれど、既存のファイルの追加
とすれば画像ファイルでもPDFファイルでもリソースとして登録してProperties.Resources.リソース名
で使えます。
とても便利ですね。
何が困るんです?
ほとんど困らないんですけれども、このやり方だと以下のようにResources
フォルダに入っていきます。
でも、 階層化したいじゃないですか。 例えばこんな風に。
で、上でやった要領でProperties.Resources.マニュアル.ThisIsManual1
というようにテキストファイルの中身を取得できるかな、というとできません。インテリセンスにマニュアル
なんていうフォルダ名は出てきません。
好き勝手放り込んだ任意のテキストファイルの中身を取得するには
ズバリ、以下の手順でファイルの中身を取得できます。
- ファイルのプロパティを開きます。
- ビルドアクションを
埋め込みリソース
にします。(英語だとEmbedded Resource) - 以下のコードで取得(雑)
var assembly = Assembly.GetExecutingAssembly(); var resourceName = "AccessToFilesInProject.Resources.マニュアル.ThisIsManual1.txt"; string manualFileContent; using (var stream = assembly.GetManifestResourceStream(resourceName)) { if (stream != null) { using (var sr = new StreamReader(stream)) { manualFileContent = sr.ReadToEnd(); } } }
コード内の変数manualFileContent
にテキストファイルの中身が入ることになります。
実行するとこんな感じです。
ファイルの中身を取得できているのがわかります。 めでたしめでたし。 ほとんどの場合、理屈はとりあえずいいからやり方さえわかればよいので、ここまでで十分かと思います。
コードの説明をしてみる
蛇足な補足ですが頑張ります。 まずは、ここですね。
var assembly = Assembly.GetExecutingAssembly();
何をやっているのでしょうか。Visual Studio上でこのGetExecutingAssembly
にマウスオーバーしてみると、以下のような説明が出ます。
「アセンブリを取得」とは何ぞ。 こちらでこのように述べられています。
ざっくり言うとexeかdllことです。
ありがとうございます。すっきりしました。
つまり、実行時におけるこの(いろいろ含めて)プログラム自身を取得するということですね。
そして、このアセンブリ
の中には当然「埋め込みリソース」に設定した目的のファイルが「埋め込まれて」いるわけで、それを読み込めばよい、と。
となれば、そのファイルを指定する必要があります。
埋め込みリソースをどうやって指定するのか
これは直感的でわかりやすいところです。
var resourceName = "AccessToFilesInProject.Resources.マニュアル.ThisIsManual1.txt";
目的のファイルの位置(論理的な)を文字列で指定することができるのですが、名前空間とそのファイルまでのフォルダ階層(パス)を「.」でつなげばよろしいのです。 つまり、
名前空間名.フォルダ1.フォルダ2......ファイル名
という形式の文字列を作ればよく、今回の例でいえば、このアセンブリ
の名前空間はAccessToFilesInProject
で、Resources
フォルダの下にマニュアル
フォルダを作り、その中にThisIsManual1.txt
というファイルを追加したので、目的のファイルの位置は上記のように書けます。
Streamで読み込むんですよ
Streamって何ぞ? こちらには、以下のようにのべられています。
ストリームというのは、ひと言で云えば「データの流れ」のことです。水が、上流から下流に流れるように、データが HDD にあるファイルからメモリ上に読み込まれる、という「流れ」を示しています。逆にファイルを書き出すときには、アプリケーションのメモリ上にあるデータを HDD にあるファイルに書き出す、という流れになります。
なるほど。 ファイルやリソースにアクセスしたければそのデータへのストリーム(経路)を開いてやり、そのストリーム経由でデータを取得したり、逆に流し込んだりする、と。
using (var stream = assembly.GetManifestResourceStream(resourceName))
{...}
つまり、ここでは目的のファイル(埋め込みリソース)へのストリームを開いているわけですね。 そして、目的のファイルの指定には上で説明した形式の文字列を使うと。
それではこのstreamからデータを取り出せばいいかというと、このStreamクラスのメソッドはbyte単位でデータを読み書きするものなので、私にはハードルが高すぎます。
ストリームは開きました。あとは手軽にデータを取得したいのです。メソッド一発で。例えばReadText()
みたいなメソッドで一発でテキストを取得したいのです。例えばです。
あります。ストリームからのデータの読み込みを便利にするクラスがあります。それがStreamReader
クラスです。
このStreamReader
クラスはストリームからデータ読み込むための便利なメソッドを持っています。
ReadToEnd
とかReadLine
といったstring
を返すメソッドがあります。
今回の埋め込みリソースのファイルからテキストを読み込みたいという目的に関して言えばReadToEnd()
メソッドがうってつけです。
using (var sr = new StreamReader(stream)) { manualFileContent = sr.ReadToEnd(); }
StreamReader
にさっき取得したストリームを渡してnew
。
これでストリームからデータを読み込むための手段ができました。
後は先ほどのReadToEnd
メソッドでファイル内のテキストを取得し、無事目的達成です。
今後の発展
今回はテキストファイルからテキストを取得しましたが、当然それ以外の形式のファイル、画像ファイルとかPDFファイルとかを埋め込んじゃったり、またはコンテンツという形でアプリケーションに持たせたりすることもあります。 それらについても書きたいな、と思っています。