プロジェクト内のテキストファイルを読み込む方法　～埋め込みリソース～

#はじめに
アプリケーションを作成するときに、何らかのテキストファイルをアプリケーション内に持たせておいて、内部での処理や表示のために使いたいことがあります。
正しい方法かどうかは置いといて、一つのやり方としてはプロジェクトのプロパティからリソースとして追加して、

![clipboard_20181012160212.png](https://qiita-image-store.s3.amazonaws.com/0/245438/e261baa8-f42f-07ed-24a2-8b37a9f51113.png)
![clipboard_20181012183336.png](https://qiita-image-store.s3.amazonaws.com/0/245438/6d14fef9-759f-8ba2-6021-1f24c6906e60.png)


コードから以下のように`Properties.Resources.リソース名`と記述してファイルの中身を読み込むことができます。

```csharp
var textFileContent = Properties.Resources.ThisIsTextFile;
```

実行すると、こんな感じです。
![clipboard_20181012182853.png](https://qiita-image-store.s3.amazonaws.com/0/245438/f547cfb3-3e92-b0c8-6b0a-a54ab2dd9bdf.png)

今回はテキストファイルですけれど、`既存のファイルの追加`とすれば画像ファイルでもPDFファイルでもリソースとして登録して`Properties.Resources.リソース名`で使えます。
とても便利ですね。

#何が困るんです？
ほとんど困らないんですけれども、このやり方だと以下のように`Resources`フォルダに入っていきます。
![clipboard_20181012184119.png](https://qiita-image-store.s3.amazonaws.com/0/245438/aaf3ca77-2bb2-dfe3-dc36-9297fef4b7a0.png)

でも、
**階層化したいじゃないですか。**
例えばこんな風に。
![clipboard_20181012185538.png](https://qiita-image-store.s3.amazonaws.com/0/245438/4d8181e5-ee53-f240-4b5c-1fe49ea9bc6a.png)

で、上でやった要領で`Properties.Resources.マニュアル.ThisIsManual1`というようにテキストファイルの中身を取得できるかな、というとできません。インテリセンスに`マニュアル`なんていうフォルダ名は出てきません。
![clipboard_20181012185829.png](https://qiita-image-store.s3.amazonaws.com/0/245438/a038a47f-6f9b-a1b4-680d-74709a1223fc.png)

#好き勝手放り込んだ任意のテキストファイルの中身を取得するには
ズバリ、以下の手順でファイルの中身を取得できます。

1. ファイルのプロパティを開きます。
![clipboard_20181012193822.png](https://qiita-image-store.s3.amazonaws.com/0/245438/714ae1ec-be47-54f2-2134-0a4d0a3f69e0.png)
2. ビルドアクションを`埋め込みリソース`にします。（英語だとEmbedded Resource）
![clipboard_20181012193953.png](https://qiita-image-store.s3.amazonaws.com/0/245438/3a6155f7-6796-9798-1c13-3b9cc9c5ad42.png)
3. 以下のコードで取得（雑）

```csharp
            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`にテキストファイルの中身が入ることになります。
実行するとこんな感じです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/a403eae0-5f4a-2eae-dc1a-b362c44e7b65.png)

ファイルの中身を取得できているのがわかります。
めでたしめでたし。
ほとんどの場合、理屈はとりあえずいいからやり方さえわかればよいので、ここまでで十分かと思います。

#コードの説明をしてみる
蛇足な補足ですが頑張ります。
まずは、ここですね。

```Csharp
var assembly = Assembly.GetExecutingAssembly();
```
何をやっているのでしょうか。Visual Studio上でこの`GetExecutingAssembly`にマウスオーバーしてみると、以下のような説明が出ます。

![clipboard_20181012195937.png](https://qiita-image-store.s3.amazonaws.com/0/245438/6b61e3a5-a142-e8c0-0cf1-6c63ee8e1762.png)

「アセンブリを取得」とは何ぞ。
[こちら](https://qiita.com/rita0222/items/609583c31cb7f0132086)でこのように述べられています。


>ざっくり言うとexeかdllことです。


ありがとうございます。すっきりしました。
つまり、実行時におけるこの（いろいろ含めて）プログラム自身を取得するということですね。
そして、この`アセンブリ`の中には当然「埋め込みリソース」に設定した目的のファイルが「埋め込まれて」いるわけで、それを読み込めばよい、と。
となれば、そのファイルを指定する必要があります。

###埋め込みリソースをどうやって指定するのか
これは直感的でわかりやすいところです。

```csharp
var resourceName = "AccessToFilesInProject.Resources.マニュアル.ThisIsManual1.txt";
```

目的のファイルの位置（論理的な）を文字列で指定することができるのですが、名前空間とそのファイルまでのフォルダ階層（パス）を「.」でつなげばよろしいのです。
つまり、

```
名前空間名.フォルダ1.フォルダ2......ファイル名
```
という形式の文字列を作ればよく、今回の例でいえば、この`アセンブリ`の名前空間は`AccessToFilesInProject`で、`Resources`フォルダの下に`マニュアル`フォルダを作り、その中に`ThisIsManual1.txt`というファイルを追加したので、目的のファイルの位置は上記のように書けます。

###Streamで読み込むんですよ
Streamって何ぞ？
[こちら](https://ameblo.jp/tony-den/entry-11176825771.html)には、以下のようにのべられています。


>ストリームというのは、ひと言で云えば「データの流れ」のことです。水が、上流から下流に流れるように、データが HDD にあるファイルからメモリ上に読み込まれる、という「流れ」を示しています。逆にファイルを書き出すときには、アプリケーションのメモリ上にあるデータを HDD にあるファイルに書き出す、という流れになります。

なるほど。
ファイルやリソースにアクセスしたければそのデータへのストリーム（経路）を開いてやり、そのストリーム経由でデータを取得したり、逆に流し込んだりする、と。

```csharp
using (var stream = assembly.GetManifestResourceStream(resourceName))
{...}
```
つまり、ここでは目的のファイル（埋め込みリソース）へのストリームを開いているわけですね。
そして、目的のファイルの指定には上で説明した形式の文字列を使うと。

それではこのstreamからデータを取り出せばいいかというと、このStreamクラスのメソッドはbyte単位でデータを読み書きするものなので、私にはハードルが高すぎます。

![clipboard_20181013105435.png](https://qiita-image-store.s3.amazonaws.com/0/245438/43049e26-787e-fc21-6f76-581778d27740.png)

ストリームは開きました。あとは手軽にデータを取得したいのです。メソッド一発で。例えば`ReadText()`みたいなメソッドで一発でテキストを取得したいのです。例えばです。

あります。ストリームからのデータの読み込みを便利にするクラスがあります。それが`StreamReader`クラスです。
この`StreamReader`クラスはストリームからデータ読み込むための便利なメソッドを持っています。
`ReadToEnd`とか`ReadLine`といった`string`を返すメソッドがあります。
![clipboard_20181013110948.png](https://qiita-image-store.s3.amazonaws.com/0/245438/6c6eb7e8-a762-1818-decc-e42d2889b1d1.png)

今回の埋め込みリソースのファイルからテキストを読み込みたいという目的に関して言えば`ReadToEnd()`メソッドがうってつけです。
![clipboard_20181013111220.png](https://qiita-image-store.s3.amazonaws.com/0/245438/29013a65-5699-98c4-70ee-4918758e519d.png)

```csharp
using (var sr = new StreamReader(stream))
{
    manualFileContent = sr.ReadToEnd();
}
```
`StreamReader`にさっき取得したストリームを渡して`new`。
これでストリームからデータを読み込むための手段ができました。
後は先ほどの`ReadToEnd`メソッドでファイル内のテキストを取得し、無事目的達成です。

#今後の発展
今回はテキストファイルからテキストを取得しましたが、当然それ以外の形式のファイル、画像ファイルとかPDFファイルとかを埋め込んじゃったり、またはコンテンツという形でアプリケーションに持たせたりすることもあります。
それらについても書きたいな、と思っています。

今回のソースコードは[こちら](https://github.com/shuheydev/AccessToFilesInProject)。