ファイルの読み込み
テキストファイルやバイナリファイルを開いてデータを読み込む。
レベル初心者
プラットフォーム:Windows, macOS, Linux
クラス: File,FileInputStream,FilenameComponent,TextEditor,String,MemoryBlock
スタート
このチュートリアルのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。
このステップでヘルプが必要な場合は、以下を参照してください。Tutorial: Projucer Part 1: Getting started with the Projucer.
デモ・プロジェクト
デモ・プロジェクトでは、ファイルを選択するためのシンプルなウィンドウが表示されます。FilenameComponentオブジェクトに変換される。このファイルが開かれ、文字列として読み込まれてTextEditorコンポーネントを使用している。
JUCEのファイル
このチュートリアルでは、JUCEを使ってファイルを読むための基本的なテクニックを説明します。異なるオペレーティングシステム上のファイルシステムは、時として全く異なる方法で動作するためです。JUCE を使用する開発者であれば、このような問題から免れることはできませんが、JUCE は異なるプラットフォーム間でより一貫した体験を提供します。
ファイルクラス
についてFileクラスは絶対ファイルやディレクトリへのパス(実際に存在するかどうかは問わない)。最も簡単な方法はFileオブジェクトを渡すことである。Stringには絶対パスが含まれる。例えば、macOS、Linux、Androidの場合、次のような絶対パスになります:
juce::File path ("/path/to/file.txt");
しかしFileクラスは、親ディレクトリからの相対パスを持つ子ファイルを要求したり、親ディレクトリを取得したりすることで、パスを操作する方法を可能にします。例えば、上のコードはFile::getChildFile()関数は次のようになる:
juce::File path (File ("/path").getChildFile ("to").getChildFile ("file.txt"));
FileRepresents a local file or directory.Definition juce_File.h:60
この例では、コードがより冗長になったが、実際のシナリオでは、同じディレクトリから複数のファイルにアクセスする必要があることはよくある。したがって、親ディレクトリを1つのFileオブジェクトを作成し、必要なときに子ファイルをリクエストする。
Windowsの場合、同等の絶対パスは次のようになる:
juce::File path ("C:\path\to\file.txt");
子ディレクトリと親ディレクトリの扱いは各プラットフォームで同じです。JUCEはプラットフォームの違い(パスの区切り文字など)を処理します。
FilenameComponentクラス
このチュートリアルではFilenameComponentオブジェクトにリスナーをアタッチすれば、標準的なオペレーティング・システム・ウィンドウを使ってユーザーがファイルを選択できるようになる。リスナーをFilenameComponentオブジェクト(参照Tutorial: Listeners and Broadcastersとして取得できる。Fileオブジェクトがある。
AFilenameComponentオブジェクトは絶対パスを含むテキストボックスを表示します。また、ユーザーがオペレーティング・システムからファイルを選択するためのボタンもあります。最近使ったファイルのリストを含むドロップダウンメニューもあります。これは使用中に自動的に入力されますが、最近使用したファイルを手動で追加することもできます(例えば、アプリケーションにハードコードしたり、環境設定ファイルから)。
すぐにわかるようにFilenameComponentコンストラクタには多くの引数があり、デフォルトのコンストラクタはありません。このような場合、子コンポーネントをstd::unique_ptrオブジェクトに格納する方が簡単なことがよくあります。(これは、コンストラクタのクラス初期化リストで初期化する必要がないことを意味します)。またTextEditorコンポーネントを使い、ファイルの内容を表示する。このTextEditorクラスするにはデフォルトのコンストラクタがありますが、一貫性を保つために、両方のコンポーネント・オブジェクトをstd::unique_ptrオブジェクトに格納します:
std::unique_ptr fileComp;
std::unique_ptr textContent;
我々のMainContentComponent
コンストラクタで新しいFilenameComponentオブジェクトを作成し、ファイルを開くのに適した設定で初期化する。FilenameComponentクラスは、ファイルを保存する場所の選択にも使用できます)。このコンストラクタの中で、選択したいファイルの接尾辞のリストを指定することができます(たとえば、以下のようになります、"*.txt;*.foo"
).また、接尾辞を強制することもできる(ファイルを保存するときに便利)。どちらの引数にも空文字列を渡しますが、これはフィルタリングが実行されないことを意味します。その他の引数は自明なものであり、フィルタリングを実行させたい他の方法を定義している。FilenameComponentオブジェクトを動作させる(コード中のコメントを参照):
MainContentComponent()
{
fileComp.reset (new juce::FilenameComponent ("fileComp",
{}, // current file
false, // can edit file name,
false, // is directory,
false, // is for saving,
{}, // browser wildcard suffix,
{}, // enforced suffix,
"Select file to open")); // text when nothing selected
addAndMakeVisible (fileComp.get());
fileComp->addListener (this);
の中でFilenameComponentListenerコールバックは、現在選択されているファイルを取得し、それをreadFile()
関数である:
void filenameComponentChanged (juce::FilenameComponent* fileComponentThatHasChanged) override
{
if (fileComponentThatHasChanged == fileComp.get())
readFile (fileComp->getCurrentFile());
}
以下の各例ではreadFile()
関数は、選択されたファイルをさまざまな方法で読み込む。しかし、結果を表示する場所が必要だ。TextEditorのコンポーネントである。MainContentComponent
コンストラクタも同様だ:
textContent.reset (new juce::TextEditor());
addAndMakeVisible (textContent.get());
textContent->setMultiLine (true);
textContent->setReadOnly (true);
textContent->setCaretVisible (false);
setSize (600, 400);
}
ファイル全体を文字列に読み込む
一方Fileクラスは主にファイルへのパスを保存し操作するために設計されているが、実に単純な方法でファイルを読み込むための便利な関数がいくつかある。たとえばFile::loadFileAsString()関数はその言葉どおり、ファイル全体をStringオブジェクトに変換されます。もちろん、選択されたファイルがテキストファイルでない場合、結果は意味をなさないかもしれません(JUCEはクラッシュしませんが)。この関数は、UTF-8 形式と UTF-16 形式の両方を検出して読み込むことができます:
void readFile (const juce::File& fileToRead)
{
if (! fileToRead.existsAsFile()) // [1]
return;
auto fileText = fileToRead.loadFileAsString();
textContent->setText (fileText);
}
選択されたファイルが実際に存在するかどうかをチェックしていることに注意してください。[1].オペレーティング・システムからファイルを選んだのだから、失敗することはないはずだが、ファイルを扱うときにはこのようなチェックをするのが良い習慣だ。アプリを実行しjuce.txt
テキストファイルResources
ディレクトリに移動します。結果は以下のスクリーンショットのようになります:

等価な関数がある。File::loadFileAsData()-ファイル全体をMemoryBlockオブジェクトがある。
ファイルを一行ずつ読む
ファイル読み込みプロセスをよりコントロールするにはFileInputStreamオブジェクトを作成します。これを行う一つの方法はFileInputStreamオブジェクトのコンストラクタに渡す。File読み込みたいファイルを表すオブジェクト[2].
についてFileInputStreamクラスはInputStreamこれは、データを読み込むストリームの基本クラスである。
以下のコードを追加する:
void readFile (const juce::File& fileToRead)
{
if (! fileToRead.existsAsFile())
return; // file doesn't exist
juce::FileInputStream inputStream (fileToRead); // [2]
if (! inputStream.openedOk())
return; // failed to open
ファイルを一行ずつ読み込むが、"*"文字で始まる行も検出する。そして、これらの行を別のフォントでフォーマットし、他のテキストの見出しとして使用します。次のコードを追加する:
textContent->clear();
auto normalFont = textContent->getFont();
auto titleFont = normalFont.withHeight (normalFont.getHeight() * 1.5f).boldened();
juce::String asterix ("*");
次の部分はinputStream
オブジェクトのwhile()
ループ、ストリームがなくなるまで[3].これを加える:
while (! inputStream.isExhausted()) // [3]
{
auto line = inputStream.readNextLine();
if (line.startsWith (asterix))
{
line = line.removeCharacters (asterix);
textContent->setFont (titleFont);
}
else
{
textContent->setFont (normalFont);
}
// append the text to the textContent
textContent->insertTextAtCaret (line + juce::newLine);
}
}
これでわかるだろう:
- を使用して行を読み取る。InputStream::readNextLine()関数である、
- で始まる行かどうかをチェックする、
- のフォントを設定します。
textContent
オブジェクトを適宜使用する、 - を削除する。
- にテキスト行を追加する。
textContent
オブジェクトがある。
同じものを積むjuce.txt
ファイルは以下のスクリーンショットのようになるはずだ:

このサブセクションの例のコードはFileReadingTutorial_02.h
ファイルにある。
ファイルをバイトごとに読む
についてInputStreamしたがってFileInputStreamまた、クラスには、ファイルを1バイトずつ細かく読み込む関数もある。これを説明するために、テキスト・ファイルを読み込んで、それぞれの単語を異なる色で表示してみよう。まず、ランダムな色を生成する関数を追加しましょう。この関数を追加すると、ランダムな色が生成されるが、明るさは指定した最小値にクリップされる(これは、黒い背景から色が見えるようにするためである):
static juce::Colour getRandomColour (float minBrightness)
{
auto& random = juce::Random::getSystemRandom();
juce::Colour colour ((juce::uint8) random.nextInt (256),
(juce::uint8) random.nextInt (256),
(juce::uint8) random.nextInt (256));
return colour.getBrightness() >= minBrightness ? colour
: colour.withBrightness (minBrightness);
}
次に、データをFileInputStreamオブジェクトをスペース文字に達するまで返します。そして、そのスペースまでのテキストを返します。これは小さなメモリー・バッファを作りMemoryBlockクラスを使用しています。InputStream::readByte()関数から1バイトずつ読み込む。inputStream
オブジェクトがある:
static juce::String readUpToNextSpace (juce::FileInputStream& inputStream)
{
juce::MemoryBlock buffer (256);
auto* data = static_cast (buffer.getData());
size_t i = 0;
while ((data[i] = inputStream.readByte()) != 0 && i < buffer.getSize())
if (data[i++] == ' ')
break;
return juce::String::fromUTF8 (data, (int) i); // [4]
}
についてString::fromUTF8() [4]関数は、生のバイナリデータをStringオブジェクトがある。
最後にreadFile()
関数を使用します。readUpToNextSpace
関数を使用して、ストリームがなくなるまでテキストファイルから単語を読み込む。以下のコードを追加する:
void readFile (const juce::File& fileToRead)
{
if (! fileToRead.existsAsFile())
return; // file doesn't exist
juce::FileInputStream inputStream (fileToRead);
if (! inputStream.openedOk())
return; // failed to open
textContent->clear();
while (! inputStream.isExhausted())
{
auto nextWord = readUpToNextSpace (inputStream);
textContent->setColour (juce::TextEditor::textColourId, getRandomColour (0.75f));
textContent->insertTextAtCaret (nextWord);
}
}
このコードを実行すると、以下のスクリーンショットのようになる。

このサブセクションの例のコードはFileReadingTutorial_03.h
ファイルにある。
を作成する別の方法がある。FileInputStreamオブジェクトはFile::createInputStream()関数を使用します。この関数はFileInputStreamオブジェクトをヒープ上に割り当てた。new
演算子である。これはつまりとても終了したらオブジェクトをデアロケートすることが重要である。理想的には、このためにstd::unique_ptrオブジェクトを使うべきである。ここでの若干の違いはFile::createInputStream()関数はnullptr
値を返します。次のコードは、この場合に使用すべき典型的なパターンを示している:
void readFile (const juce::File& fileToRead)
{
if (! fileToRead.existsAsFile())
return; // file doesn't exist
if (std::unique_ptr inputStream { fileToRead.createInputStream() })
{
textContent->clear();
while (! inputStream->isExhausted())
{
auto nextWord = readUpToNextSpace (*inputStream);
textContent->setColour (juce::TextEditor::textColourId, getRandomColour (0.75f));
textContent->insertTextAtCaret (nextWord);
}
}
}
行末の単語の色が、次の行頭の単語の色と同じであることに気づくかもしれない。これは、区切り文字としてスペース文字だけを探しているためです。改行、キャリッジ・リターン、タブ文字も探すようにコードを修正してください。(これらは文字'\n'
,'\r'
そして'\t'
.)
バイナリデータの読み取り
このチュートリアルでは、ファイルからの文字列データの読み取りについて見てきた。ファイルから1バイトを読み取るだけでなくInputStreamクラスには、他の基本型を読み取るための関数も含まれています。例えば
- InputStream::readInt()- を読む。
int
(32ビット)をストリームから取得する。 - InputStream::readShort()- を読む。
short
(ストリームからの(16ビット)整数。 - InputStream::readFloat()- を読む。
float
(32ビット)をストリームから取得する。
これらはすべて、リトルエンディアンバイトオーダーでマルチバイト値を読み込む。ビッグエンディアンの値として読み込むには、別のバージョンがある。InputStream::readIntBigEndian()関数を使用する。また、ストリームからデータ・ブロックを読み込むにはInputStream::read()またはInputStream::readIntoMemoryBlock()の機能がある。
これらは、既存のファイルをバイナリ形式で読み込む必要がある場合や、データをバイナリとして保存する必要が本当にある場合に便利です。たいていの場合は、XMLを使うのが望ましいだろう(XMLではXmlDocumentそしてXmlElementクラス)またはJSON(varオブジェクトにデータを格納することによって)データを格納したり読み取ったりすることができます。
概要
このチュートリアルでは、様々な方法でテキストファイルを読むことを通して、JUCEを使用した簡単なファイル読み取りのテクニックを紹介しました。特に、以下のことができるようになるはずです:
- を使用する。FilenameComponentそしてFilenameComponentListenerファイルパスを保存し、ユーザーにファイルを選択する手段を提供する。
- ファイルの内容全体をStringオブジェクトがある。
- 適切なInputStreamの機能がある。