小学校教員のためのプログラミング入門

データを読み書きする

文字を表示する

 キーボードの文字キーが押されると,その文字を表示するスケッチを作ってみます.

  表示する文字の取得

 キーボードの文字キーが押されたとき,その文字を取得するには,前回説明したようにイベントハンドラ keyPressed を利用します.なお,文字を格納する変数の型は char です.最初は何も表示しないようにするために,スペースを代入しています.

char keydata = ' ';
      :
void keyPressed()
{
  keydata = key;
}

  フォントの準備

 Processing で文字を表示するには,表示する文字のフォントを生成する必要があります.そのためには,PDE のメニューから,[Tool] - [CreateFont] を選びます.するとダイアログが表示されますので,使いたいフォントの種類とサイズを指定します.

 今回は ArialMT の 24 ポイントを使うことにしてみます.また,どのような文字のフォントを作るかを For... と表示されているところに入力します.今回は英数字を対象とするので,ABCD...XYZabcd...xyz0123456789 あたりを入力します.もしくは [Characters] をクリックした後,[Default Characters] にチェックを入れるのでも構いません.また,下に表示される Filename をコードの中で利用しますので,記録しておいてください.

  文字表示の準備

 作成したフォントを用いて表示をするには PFont クラスのインスタンスを生成します.そこで,PFont オブジェクトを格納する変数をグローバル変数として宣言し,setup の中でオブジェクトを生成し,格納するコードを入れます.

PFont font;
  :
void setup()
{
  :
   font = loadFont("ArialMT-24.vlw"); 

  文字の表示

 文字の表示は次のようなコードで行えます.

fill(255);
textFont( font );
text( keydata, 50, 50 );

 fill は塗りつぶし色を指定するメソッドでした.文字の色は fill メソッドで指定します.textFont メソッドはフォントの種類を指定するメソッドで,生成したPFont オブジェクトをパラメータにします.text メソッドは 1 番目パラメータとして渡した文字列を画面上に描くメソッドです.2,3 番目のパラメータで表示する位置を指定します.

 次に,ここまでの説明を元に作成したスケッチを示します.

PFont font;
void setup()
{
  font = loadFont("ArialMT-24.vlw");
}

void draw()
{
  fill(0);
  stroke(0);
  rect(0,0,width,height);
  
  fill(255);
  textFont( font );
  text( keydata, 50, 50 );
}

char keydata = ' ';
void keyPressed()
{
  keydata = key;
}

文字の表示はもちろん draw メソッドの中に入れます.また,毎回文字を描く前に,前に描いた文字を消すために,描画ウィンドウ内を黒で塗りつぶしています.

 番外編として,データの読み書きについて説明します.教材を作っているときに,データを保存したり,ファイルに書かれているデータを読み込んでなんらかの処理をするということが必要になるかと思います.

 今回説明する方法は,ローカルファイル,つまり,作ったプログラムを実行するコンピュータ内に保存されているファイルに対して読み書きする方法です.このため,データの書き込みに関しては,Applet として生成し実行した場合には,この方法は利用できません.

テキストファイル生成スケッチ

14-1.jpg

  タイプライタースケッチ

次のコードは,キーをタイプすると,押されたキーに対応する文字を表示するスケッチです.特別な処理をしていませんので,TAB キーや BS キーが押されることにた対応していません.

PFont font;  // フォントオブジェクトを格納する変数の宣言
String data;  // 文字列を格納する変数の宣言

void setup()
{
  size(300,300);  // 描画ウィンドウのサイズは300×300
  background(0);  // 背景は黒
  font = loadFont("Aharoni-Bold-24.vlw");  // フォントファイルの読み込み
  textFont(font);  // フォントの設定
  data = "";  // 文字列をからっぽにする
}

void keyPressed()
{
  // キーが押されたら,文字列にそのキーに対応する文字を追加
  data = data + key;
}

void draw()
{
  fill(0);  // 黒色で
  rect(0,0,width,height);  // 描画ウィンドウ全体を塗りつぶす  
  
  fill(255);  // 白色で
  text( data, 0, 30 );  // 描画ウィンドウに文字列を表示
}

  打ち込んだテキストの保存

 では,このコードを変更して,打ちこんだテキスト(文字列)をファイルに保存してみます.

 ファイルにデータを書き込むには PrintWriter クラスを用います.そこで,まず PrintWriter オブジェクトを格納するための変数をグローバル変数として宣言します.

PrintWritter output;

 次にデータを書き込むファイルを作成します.ファイルの生成には createWriter メソッドを利用します.パラメータはファイル名で,戻り値が PrintWriter オブジェクトになります.今回は setup の中で生成をすることにします.

output = createWriter( "typewriter.txt" );

 データを実際に書き込むときには,PrintWriter クラスのメンバメソッドである print メソッド,および,println メソッドを用います.両者ともパラメータは表示したい値です.後者はデータを書き込んだ後,改行コードも記録してくれます.

 なお,print と println メソッドを呼び出すだけでは,バッファと呼ばれるメモリ上に記憶されるだけで,物理的な記録媒体(たとえばハードディスク)には記録されません.この記録を行いたいときには,同じくメンバメソッドの flush を呼びます.物理的な記録には時間がかかるため,ある程度のデータをバッファにためてから,まとめて物理的に記録する仕組みが用意されているのです.

 今回はキーが押されるたびに,そのキーに対応する文字を書き込むことにします.ここで,改行が押された場合,ファイルの方にも改行コードを記録したいと思います.しかし,Enter キーがおされたときに key に格納されているデータを print メソッドで記録しようとしてもうまくいきません.そこで,Enter キーが押されたときには println メソッドを呼び出すことで,改行コードを記録します.Enter キーが押されたかどうかは,key に格納されている値(文字)を int 型に変換してあげます.すると,文字コードが出力されるので,Enter キーに対応する文字コード 10 であるかどうかを判定すればよいことになります.また,Enter キーが押されたタイミングで物理的な記録をすることにしましょう.

if( int( key ) == 10 )
{
  output.println( "" );
  output.flush();
}
else
{
    output.print( key);
}

 データを書き終えたら,必ずメンバメソッド close を呼び出します.今回は,プログラム終了時に呼び出される stop メソッドの中で行えばよいでしょう.

void stop()
{
  output.close();
}

 上記をまとめると次のようなコードになります.

PFont font;
String data;
PrintWriter output;  // PrintWriter オブジェクトを格納する変数

void setup()
{
  size(300,300);
  background(0);
  font = loadFont("Aharoni-Bold-24.vlw");
  textFont(font);
  data = "";
  
  output = createWriter("typewriter.txt");   // データ書き込み用ファイルを生成
}

void keyPressed()
{
  data = data + key;
  
  if( int( key ) == 10 )  // Enter キーが押されたら
  {
    output.println( "" );  // 改行コードを書き込み
    output.flush();  // 物理的な書き込み
  }
  else
  {
    output.print( key);  // 押されたキーに対応する文字を書き込み
  }
}

void stop()
{
  output.close();  // 生成したファイルへの書き込みを終了
}

void draw()
{
  fill(0);
  rect(0,0,width,height);
  
  fill(255);
  text( data, 0, 30 );
}

 このスケッチを実行すると,実行フォルダ(PDE 環境で実行した場合は pde ファイルと同じ場所,ウィンドウズアプリケーションとして実行した場合は exe ファイルと同じ場所)に typewriter.txt というファイルが生成されます.それを開くと,打ち込んだテキスト(文字列)が書き込まれていることを確認できると思います.

テキスト表示スケッチ

 次に示すのは,上で作ったスケッチの逆,ファイルに書き込まれているテキスト(文字列)データを読み込み,画面に表示するスケッチです.

void setup() 
{
  size(300,300);
  background(0);
  
  PFont font = loadFont("Aharoni-Bold-24.vlw");
  textFont(font);
  
  noLoop();  // draw メソッドを呼び出さないようにする
  read();
}

void read()
{
  String data = "";
  String line = "";
  BufferedReader reader = createReader("typewriter.txt");  // 読み込むファイルを設定
  
  while( line != null )  // すべて読み込む(line に null が設定される)までループする
  {
    try 
    {
      line = reader.readLine();  // 1行読み出す
    } 
    catch (IOException e) 
    {
      line = null;  // もしエラーが発生したら,null を設定する
    }
    if( line != null )  // 1行読み出すことに成功したら
    {
      data += line + '\n';  // その1行と改行を,data に加える
    }  
  }

  text( data, 0, 30 );  

  reader.close();
}

  ファイルからの読み出し

 テキストファイルを読み込むには,BufferedReader クラスを使います.createReader メソッドで読み込みたいファイルを指定し,戻り値の BufferReader オブジェクトの readLine メソッドを呼び出すと,そのファイルから1 行の文字列を読み出せます.

 ファイルからすべて文字列を呼び出した後に,再度readLine メソッドを読み出すと,null が戻り値として帰ってきます.そこで,読みだしたデータが null になるまで繰り返し readLine メソッドを呼び出すことで,ファイル内のすべてのデータを読み出すことができます.

 readLine で読み出したデータは String 型の変数 data に加えていっています.読みだしているデータは1行単位なので,加えるときに '\n' を後ろに加えて,表示の際に改行されるようにしています.'\n' は改行を意味する文字です.そして,すべて読み出したら,描画ウィンドウに text メソッドで描画を行っています.

  読み出し時の例外エラー対処

 readLine は 読み込むタイミングによって例外エラーというエラーが発生することがあります.例外エラーが発生するとプログラムの実行が止まってしまいますので,try〜catch 構文というものを使い,この停止を避けることを行います.これは try の部分で例外エラーが発生したら,プログラムの実行は停止させない代わりに,catch の部分のコードを実行するというものです.今回は readLine でエラーが発生したら,変数 line に null を設定しています.こうすることで,エラーが発生したときは,すべてのデータを読み出したと同じとみなして,処理を続けさせています.

  draw メソッドの常時呼び出しの停止

 このスケッチでは,noLoop というメソッドを呼び出しています.連続モードでは draw メソッドが常に呼び出されることになっていますが,noLoop メソッドを呼び出すと,その呼び出しが停止します.このスケッチでは,ファイル内のすべてのデータを読み込んだら,それを描画すればよいだけなので,描画は1回だけで十分です.そのため,draw メソッドの呼び出しを停止して,setup メソッドから呼び出している read メソッド(自分で作成したメソッド)内で,描画を行っています.

 なお,draw メソッドの呼び出しを再開したい場合は,loop メソッドを呼び出します.また,停止中に draw メソッドを1回だけ呼び出したい場あは redraw メソッドを呼び出します.

その他のデータの読み書き方法

  文字列の配列の読み書き

 次のスケッチは,apple,orange,grape という単語を fruits.txt というファイルに保存するものです.

String[] list = new String[3];
list[0] = "apple";
list[1] = "orange";
list[2] = "grape";

saveStrings("fruits.txt", list);

exit();

 saveStrings メソッドは,第1パラメータで受け取った名前のファイルを生成し,第 2 パラメータで受け取った文字列の配列の内容を,1要素を1行として書き込みます.

 なお,メソッド exit はプログラムを終了させるもので,このスケッチの場合,描画ウィンドウを表示し続けても意味がないので,強制的に終了し,ウィンドウを閉じています.

 この逆に,ファイルに書き込まれた文字列を配列に読み込むメソッドが loadStrings です.次のコードは fruits.txt に書き込まれている内容を,文字列の配列 lines に読み込みます.1行が1要素に格納されます.lines のメンバ変数 length には配列 lines の要素数を格納されています.

String lines[] = loadStrings("fruits.txt");
for (int i=0; i < lines.length; i++) 
{
  println(lines[i]);
}

 メソッド println は,PDE の下側にあるところにデータを表示させるメソッドです.先のスケッチで生成したファイルを,上記スケッチの PDE ファイルと同じ場所にコピーした後,上記スケッチを実行すると,apple,orange,grape が 1 行ずる表示されるはずです.

  バイナリデータの読み書き

 次のスケッチは,saveBytes メソッドを用いて,byte 型の配列データをファイルに書き込むものです.byte 型は -128〜127 を格納することができるデータ型です.

byte[] nums = { 0, 34, 5, 127, 52};

saveBytes("numbers.dat", nums);

exit();
byte[] nums = { 0, 34, 5, 127, 52};

byte[] nums = new byte[5];
nums[0] = 0;
nums[1] = 34;
  :

をまとめて書いた書式です.

 逆に,ファイルから byte 型の配列にデータを読み込むスケッチは次のようになります.loadBytes メソッドはファイルに書き込まれているデータを格納した byte 型の配列を生成して戻します.

byte b[] = loadBytes("numbers.dat"); 
 
for (int i = 0; i < b.length; i++)
{ 
    println( b[i] );
} 

exit();

  Int 型のデータを読み書きする

 int 型の配列を書き込むには次のように byte 型に変換をすることで行います.

int[] nums = { 300, 301, 302 };
byte[] bytes = new byte[3*4];

for( int i = 0 ; i < nums.length ; i++ )
{
  for( int j = 0 ; j < 4 ; j++ )
  {
    bytes[i*4+j] = byte( ( nums[i]>>8*(3-j) ) & 0xff );
  }
}

saveBytes("numbers.dat", bytes);

exit();

 nums[i]>>8*(3-j) は 32bit の長さの int 型のデータを j=0 のときは 24bit,j=1 のときは 16bit,j=2 のときは 8bit ずらす処理です.その値に対して 0xff(=11111111)との論理積をとりますので,つまり,j=0のときは最上位の 8bitを,j=1 のときは次の 8bit を,j=2 のときはさらに次の 8bit を,そして,j=3 のときは最下位の 8bit を取り出すことになります.それを byte 型に変換して,byte 型の配列に代入しています.

 一方,読み込みの方は,