プロジェクト内のテキストファイルを読み込む方法　～Resource～

#いきなり追記（2018.10.15）
@Zuishinさんより教えていただいた方法だと、ビルドアクションに`Resource`を設定したファイルに非常にスマートにアクセスできます。@Zuishinさんありがとうございます。

さて、ファイルの追加、ビルドアクションの設定の仕方は僕の書いたほうを読んでいただくとして、スマートな方法は以下のとおりです。

```csharp
//@Zuishinさんに教えていただいた方法
string fileCount;
var info = Application.GetResourceStream(new Uri("/TextFiles/TextFile1.txt",UriKind.Relative));
using (var sr = new StreamReader(info.Stream))
{
    fileContent = sr.ReadToEnd();
}
```
波括弧を除けば3行でファイルの中身を取得できます。スマートです。

頑張って説明してみます。

`Application.GetResourceStream()`メソッドでファイルへのストリームを取得、と思いきやこのメソッドの返り値は`StreamResourceInfo`です。が、この返り値がファイルへの`Stream`を持っていますので、それを使って`StreamReader`でファイルの中身を取得します。

さて、`GetResourceStream()`メソッドですが、引数に目的のファイルの位置をUriで受け取ります。Uriの部分は、下のようにソリューションエクスプローラにおいて、プロジェクトフォルダを起点としたパス文字列を構成し、`UriKind.Relative`で「相対パスですよ」と指定します。

```csharp
new Uri("/TextFiles/TextFile1.txt",UriKind.Relative)
```

後は前述のとおりStreamReaderを使ってファイルの中身を取得します。

素晴らしくお手軽ですね。
以上です。
この方法を追加したプロジェクトは[こちら](https://github.com/shuheydev/C-Users-shuhei-Desktop-AccessToResourceInProject.git)


#はじめに
Visual Studioでアプリケーションのプロジェクトにフォルダやファイルを追加できますが、プログラム中からそれらのファイルにどうやってアクセスするか、の第2弾です。
ファイルのプロパティでビルドアクションを設定できるのですが、`埋め込みリソース`や`Resource`、`コンテンツ`と設定することでプログラムからそれらのファイルにアクセスすることができます。
[以前](https://qiita.com/shuhey/items/646f285d3a2d9705c98e)に`埋め込みリソース`なファイルへのアクセスの仕方について説明しましたが、今回は`Resource`なファイルへのアクセスの仕方について説明します。

#ファイルを追加します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/f915dda1-a700-aa26-8288-ec1ddc225d5b.png)

`TextFiles`フォルダを追加して、その中に`TextFile1.txt`を追加します。`TextFile1.txt`に何か適当なものを書き込んでおきます。

#ファイルのビルドアクションを`Resource`に設定します
ソリューションエクスプローラ上で`TextFile1.txt`のプロパティを開いて、ビルドアクションを`Resource`に設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/59529587-69e9-9ca9-eed5-7b3a46a87833.png)
通常、ファイルを追加した場合はデフォルトで`Resource`になっています。


#まずは一度コンパイルします
ビルドアクションに`Resource`を設定したファイルはコンパイルされて`アプリケーション名.g.resources`というファイルにバイナリデータとして書き込まれます。
このファイルはビルドすると作成されるので、まずは一度ビルドしてみましょう。
ビルドが正常終了したら以下のフォルダ内に先ほどの`アプリケーション名.g.resources`ファイルができていることが確認できます。
```
ソリューションフォルダ\プロジェクトフォルダ\obj\Debug
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/81c6c748-dc9f-ef35-c509-fdc5e6670c34.png)

この中に`Resource`に指定したテキストファイルがバイナリデータで入っているはずです。

#`アプリケーション名.g.resources`ファイルへのストリームを開く
何はともあれ、ファイルがあればそこへのストリームを開きます。
このプロジェクトに含まれるファイルへアクセスする場合、まずはアセンブリ（今書いているプロジェクトがコンパイルされて出来上がるexeやdllのこと）へアクセスる手段を取得します。

```csharp
public MainWindow()
{
    InitializeComponent();

    var assembly = Assembly.GetExecutingAssembly();
}
```
`GetExecutingAssembly()`わかりやすいメソッド名ですね。実行中のアセンブリを取得しろ、と。

そして、`アプリケーション名.g.resources`へのストリームを開きます。

```Csharp
using (var stream = assembly.GetManifestResourceStream("AccessToResourceInProject.g.resources"))
{
・・・
}
```

#追加したテキストファイルへアクセスする
ここまでで`アプリケーション名.g.resources`ファイルへアクセスできるようになりました。
では、このファイルの中に入っているだろうテキストファイルへはどのようにアクセスしたらよいのでしょうか。
ここで使用するのが`System.Resources.ResourceReaderクラス`です。
このクラスを使用して`アプリケーション名.g.resources`ファイル内のリソースへアクセスするためのストリームを開きます。

```Csharp
using (var stream = assembly.GetManifestResourceStream("AccessToResourceInProject.g.resources"))
{
    if (stream != null)
    {
        using (var rr = new ResourceReader(stream))
        {
　　　　　　・・・
        }
    }
}
```

この変数`rr`が`アプリケーション名.g.resources`ファイル内のリソースファイルへアクセスするための手段となるわけです。
このResourceReaderというクラス、名前からしてさぞResourceなファイルへのアクセスを楽にしてくれそうですね。

#ファイルへアクセスします
インテリセンスによると、やはりそのものずばりといったメソッドがあります。`GetResourceData()`。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/b0f4de06-2e2b-0ddf-3fec-e2bdde5b2fcf.png)

もうゴールは目の前な気がします。
`GetResourceData()`を使う場合、以下のようになります。
`resourceName`にはフォルダ名とファイル名を**全部小文字**にしてリソースへのパスを作って入れておきます。
ここで気を付けることはもう一つあって、フォルダ名とファイル名を「.」でつなぐのではなく「/」でつなぐということ。

```csharp
string fileContent = "";
using (var stream = assembly.GetManifestResourceStream("AccessToResourceInProject.g.resources"))
{
    if (stream != null)
    {
        using (var rr = new ResourceReader(stream))
        {
            if (rr != null)
            {
                var resourceName = "textfiles/textfile1.txt";
                string type;
                byte[] resourceData;
                rr.GetResourceData(resourceName,out type,out resourceData);
                
                if (resourceData != null)
                {
                    fileContent = Encoding.UTF8.GetString(resourceData);
                }
            }
        }
    }
} 
```

GetResourceData()に必要な引数を渡して呼び出すと、resourceDataの中に指定したファイルの中身がbyte配列で入ります。
今回はテキストファイルなのでそれを文字列に変換すると以下のような値が取得できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245438/352a8699-85d5-586b-8663-652bfade7f83.png)

**あれ？**
ファイルの中身は取得できているようですが、なんだか様子がおかしいです。
取得した文字列の先頭に`J\0\0\0`がついています。
これが何なのかはわかりませんが、ほしいのはこの4バイト分を除いた文字列なので、その場合は以下のようにして先頭4バイトを除いたデータを文字列に変換するとよいかもしれません。

```csharp
fileContent = Encoding.UTF8.GetString(resourceData.Skip(4).ToArray());
```



おあつらえ向きと思ったGetSourceData()ですが、それほど使いやすくはないように感じました。


#もう一つ

```csharp
string fileContent = "";
using (var stream = assembly.GetManifestResourceStream("AccessToResourceInProject.g.resources"))
{
    if (stream != null)
    {
        using (var rr = new ResourceReader(stream))
        {
            if (rr != null)
            {
                var resourceName = "textfiles/textfile1.txt";

                foreach (DictionaryEntry resource in rr)
                {
                    if ((string) resource.Key == resourceName)
                    {
                        using (var sr = new StreamReader((Stream) resource.Value))
                        {
                            fileContent = sr.ReadToEnd();
                        }
                    }
                }
            }
        }
    }
}
```

`rr`はResourceReaderのインスタンスですが、これをforeachで回すと、DictionaryEntryクラスのデータを順に取得することができます。
このDictionaryEntryクラスのデータがキー/バリューのペアになっているので、Keyプロパティが目的のファイルと同じパス文字列かをチェックし、そうであればValueプロパティがStreamクラスのデータなので、StreamReaderと合わせてReadToEnd()で中身のテキストを取得できます。

全然お手軽ではないですね。

ビルドアクションに`埋め込みリソース`を指定して、[ここ](https://qiita.com/shuhey/items/646f285d3a2d9705c98e)の手順でファイルを扱ったほうが楽な気がしました。


#おわりに
なんだか思ってたんと違う感じになってしまいましたが、ビルドアクションに`Resource`を設定したファイルへのアクセス方法の一つはわかりました。
しかし、これだけ面倒ということは、まず間違いなく僕のアプローチが間違っているはずです。
正しい方法があれば、それを書きたいと思います。


今回使ったプロジェクトは[こちら](https://github.com/shuheydev/C-Users-shuhei-Desktop-AccessToResourceInProject.git)