shuhelohelo’s blog

Xamarin.Forms多めです.

プロジェクト内のテキストファイルを読み込む方法 ~埋め込みリソース~[引越記事]

はじめに

(注:AssemblyクラスはSystem.Reflection名前空間です)

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

clipboard_20181012160212.png clipboard_20181012183336.png

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

var textFileContent = Properties.Resources.ThisIsTextFile;

実行すると、こんな感じです。 clipboard_20181012182853.png

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

何が困るんです?

ほとんど困らないんですけれども、このやり方だと以下のようにResourcesフォルダに入っていきます。 clipboard_20181012184119.png

でも、 階層化したいじゃないですか。 例えばこんな風に。 clipboard_20181012185538.png

で、上でやった要領でProperties.Resources.マニュアル.ThisIsManual1というようにテキストファイルの中身を取得できるかな、というとできません。インテリセンスにマニュアルなんていうフォルダ名は出てきません。 clipboard_20181012185829.png

好き勝手放り込んだ任意のテキストファイルの中身を取得するには

ズバリ、以下の手順でファイルの中身を取得できます。

  1. ファイルのプロパティを開きます。 clipboard_20181012193822.png
  2. ビルドアクションを埋め込みリソースにします。(英語だとEmbedded Resource) clipboard_20181012193953.png
  3. 以下のコードで取得(雑)
            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

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

コードの説明をしてみる

蛇足な補足ですが頑張ります。 まずは、ここですね。

var assembly = Assembly.GetExecutingAssembly();

何をやっているのでしょうか。Visual Studio上でこのGetExecutingAssemblyにマウスオーバーしてみると、以下のような説明が出ます。

clipboard_20181012195937.png

アセンブリを取得」とは何ぞ。 こちらでこのように述べられています。

ざっくり言うと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単位でデータを読み書きするものなので、私にはハードルが高すぎます。

clipboard_20181013105435.png

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

あります。ストリームからのデータの読み込みを便利にするクラスがあります。それがStreamReaderクラスです。 このStreamReaderクラスはストリームからデータ読み込むための便利なメソッドを持っています。 ReadToEndとかReadLineといったstringを返すメソッドがあります。 clipboard_20181013110948.png

今回の埋め込みリソースのファイルからテキストを読み込みたいという目的に関して言えばReadToEnd()メソッドがうってつけです。 clipboard_20181013111220.png

using (var sr = new StreamReader(stream))
{
    manualFileContent = sr.ReadToEnd();
}

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

今後の発展

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

今回のソースコードこちら