パソコン活用研究5番街(Visual Basic、Excel(VBA)、BASIC プログラミング研究)
ファイルの操作(シーケンシャルファイルその1)
ファイル操作がこなせるようになれば、BASICも初級レベル卒業ではないでしょうか。
ゲームではあまりファイル操作をすることはありませんが、実用プログラム(Basicで
実用プログラムができるの?という疑問が聞こえてきそうですが)では、ファイル操作が
こなせないと話にならないでしょう。
ファイルは大きくわけてシーケンシャルファイルとランダムファイルに大別されます。
シーケンシャルファイルは、各データをファイルの頭から順に記録・再生していく方式です。
ファイルを読み込む時も、頭から順に読み込んでいきます。
ランダムファイルは、任意の位置へのデータの読み書きができる方式です。
まずは扱いやすいと思われるシーケンシャルファイルの取り扱いからとりあげてみてみたいと
思います。
1 ファイルのオープン
(1) ファイルのオープン
例えば、生徒の氏名とテストの点を記入したファイルrank.txtがあるとしましょう。
このファイルからの読み込み、あるいは新たな生徒の点数の書き込みをしたい場合、
このファイルを扱えるようにしなくてはなりません。このファイルを扱えるようにする操作
を「ファイルをオープンする」といいます。
ファイルをオープンするには、OPEN命令で、どのファイルを扱いたいのか(ファイルディスクリプタ)、
読み込みたいのか、書き込みたいのか(モードの指定)、ファイル入出力にどのバッファを使うのか
(ファイル番号の指定)をします。
ファイルディスクリプタとかバッファとか初めて聞く人にはとりつきにくい用語がでてきますが、
特に難しいものではありません。ファイルディスクリプタとかいかめしい名前の用語ですが、要する
に「ファイル名」と考えてもらって結構です。
また、いちどきに複数のファイルを開いて使用することがありますが、どのファイルに対して読み書き
するのかわかりやすくするために、オープンしたファイルにはファイル番号をつけて管理します。
ファイル番号は管理のために、オープンしたファイルに番号をつけたものと考えておいて下さい。
以後、ファイルへの読み書きの時には、いちいちファイル名を書いたりせず、全てファイル番号で
どのファイルへの読み書きかを指定します。
バッファについては、ファイルバッファで説明しますので、ご参照下さい。
(2) OPEN文
ファイルをオープンする命令がOPENです。このOPEN文もBasicによって少しづつ書式や仕様が
異なる厄介な命令です。
とりあえず、N88Basic, N88互換Basic for WinのOPEN文についてとりあげてみます。99Basicや
QBasicでもだいたい同じです。
書式は、OPEN "ファイルディスクリプタ" FOR モード AS #ファイル番号 です。
例えばrank.txt を入力モードでオープンし、ファイル番号を1にするなら
OPEN "rank.txt" FOR INPUT AS #1
となります。
先ほどは、ファイルディスクリプタ≒ファイル名、と言いましたが、細かいことを言うと、ファイル
ディスクリプタには、RS232C回線(COM:)、キーボード(KYBD:)、スクリーン(SCRN:)、プリンタ
(LPT:)なども記述できます。これによって、ユーザーの入力したデータをスクーリンに出すか
プリンタに打ち出すか切り替えたり、といったことができます。詳細は別の機会に取り上げたいと
思います。
モードはシーケンシャルファイルの場合以下の3つになります。
INPUT 入力モード。既存のファイルから入力を行います。指定したファイルが存在しないと
エラー"File not Found"になります。
OUTPUT 出力モード 新しくファイルを作成し出力します。同一ファイル名があった場合は、既存
のファイルは削除されます。
APPEND 追加出力モード。 既存ファイルの終わりから追加出力を行います。N88Basicの場合
は指定したファイルがないと"File not Found"エラーになりますが、N88互換Basic
for
Winの場合は、新しくファイルを作成するよです(OUTPUTモードと同じ働きになる)
99Basic, QBasicでは上記の書式、仕様部分はそのままあてはまりますが、少し追加の機能が
あります。
99Basicでは、モードの指定で、OUTPUT,INPUT,APPEND が併記できたりします。
QBasicでは、モードにBinaryモードがあり、またネットワーク環境で使用する場合に記述する
ACCESS, LOCKというようなオプションがあります。詳しくはそれぞれの仕様書を確認して
下さい。
F-Basicでは、若干書式が異なり
OPEN "モード", #ファイル番号, "ファイルディスクリプタ"
となります。
モード指定は以下の通りです。
I 入力モード
O 出力モード。 同一ファイル名が既に存在すると、エラーになります。
A 追加出力モード
2 INPUT # , PRINT #
ファイルからのデータ読み込み、書き込みには、INPUT #, PRINT # を使います。
#でファイル番号を指定する以外は、通常のINPUT, PRINTと同じです。
例えば
INPUT #1, A,; B; C
PRINT #2, A$; ","; B$;","; C$
のように使います。
さて、ここが重要なポイントですが、ファイルの中で各データは区切り記号で区切られて
います。(Basicによって使用される区切り記号が微妙に違うので気をつけて下さい。)
データを正しく書き込み、それを正しく読み込むのには、データを適切に区切って書き込み
(PRINT #)、読み込む時のデータの変数の型、個数を正しく記述(INPUT #)する必要が
あります。ここで、区切り記号の入れ方や、読み込み時の変数の型、個数をミスすると、
正しいデータの入出力ができません。場合によってはエラーでプログラムが中断します。
ですので、各BASICのINPUT #, PRINT #の仕様ははっきりと把握しておいて下さい。
以下に代表的なBASICの場合について仕様を記載します。
N88Basicの区切り記号は、数値データの区切り記号としては、空白、コンマ、キャリッジリターン
(CHR$(13)が使用され、文字列の区切りにははコンマ、またはキャリッジリターンが使用されてい
ます。
PRINT # で数値を出力する場合、
A=123: B=456: C=-789
PRINT #1, A; B; C
のようにセミコロンで区切ります。
そうすると、ファイルの中にデータは1個の空白で区切られて、次のように書き込まれます。
123 456 -789
このデータを読み込む時は
INPUT #2, A, B, C
とすると、Aには123、Bには456、Cには-789が読み込まれます。
出力するデータが文字列の場合には、ちょっと複雑です。空白は文字列の区切り記号として
認識されないので、データとデータの間にコンマを区切り記号として入れてやる必要があります。
例えば、
A$="CAMERA": B$="9600"
PRINT #1, A$; ","; B$
のように記述します。
この場合、ファイルの中には、
CAMERA,9600
のように書き込まれます。
これを、
PRINT #1, A$; B$
と記述すると、ファイルには
CAMERA9600
と書き込まれてしまいますので、ひとつの文字列になってしまいます。
更に注意が必要なのは、文字列それ自身の中に、コンマやキャリッジリターンなどの区切り記号
を含む場合です。
A$="CAMERA, AUTOMATIC": B$="9600"
PRINT #1, A$; ","; B$
とすると、ファイルには
CAMERA, AUTOMATIC,9600
と書き込まれます。
これを
INPUT #2, A$, B$
で読み込むと、A$="CAMERA" B$=" AUTOMATIC" となり9600は読み込まれずに残ってしまい
ます。
このような場合は、ダブルクォーテーション(CHR$(34))で文字列を囲んで出力する必要があります。
例えば、
PRINT #1, CHR$(34); A$; CHR$(34); CHR$(34); B$; CHR$(34)
としてやると、ファイルには
"CAMERA, AUTOMATIC""9600"
と書き込まれます。
こうすれば、INPUT # で読み込む時に、ダブルクォーテーションで囲まれた部分をひとつの文字列
として読み込みますので、正しく読み込みができるようになります。
F-Basicでは、コンマ、コロン、キャリッジリターン、ラインフィードがファイルの中でデータの区切り
記号になります。
また、PRINT# 1, INPUT# 1 のように#をPRINTやINPUTにくっつけて記述します。
またPRINT# で文字列を出力する場合、自動的にコンマを出力する仕様になっているので、
N88Basicのように、コンマを自分で記述してやる必要がありません。
ここら辺はBASICによって微妙に仕様が違うところなのでご注意下さい。99BasicやQBasicは
N88Basicに近いような気がしますが、詳細な仕様書がないので各自ご自分で試してみて下さい。
またN88互換BASIC for Winのver2.15では、PRINT #1, N$; ","; S のように文字変数、数値変数
と出力した時に、後ろの数値変数が前の区切り記号を消すような形で出力される?というような
(これはバグかな)クセがありますので、PRINT #1, N$; " , "; S
のように区切り記号に余計な空白
を追加してやるなどの工夫が必要です。
3 ファイルのクローズ
OPENにより開いたファイルを閉じるのがCLOSEです。書式はシンプルで、以下の通りです。
【書式】 CLOSE #ファイル番号
ちなみに、#ファイル番号を省略すると、たいていのBASICでは、開いているファイルを全て閉じる、
という動作になります。
書式はいたって簡単ですが、ファイルのクローズは重要な操作です。ファイルのクローズは、よく
本を閉じる動作に譬えて説明されますが、実際はもっと重要な操作で、ファイルをクローズすること
により、以下が実行されます。
@クローズされたファイルへの入出力(読み書き)はできなくなります。再度、入出力したいと時は、
再度OPENします。
Aクローズしたファイル番号は、他のファイルの入出力に使用することができます。
Bファイルが出力用に開かれていた場合は、クローズにより、バッファに残っていたデータが完全
にファイルに書き出されます。
@により、閉じたファイルへの読み書きができなくなりますので、うっかりファイルに誤った書き込み
をしてしまうということを防止できます。プログラムミスでファイルをCLOSEし忘れて、おかしなデータ
を書き込んでしまう、なんてことが起こったらたいへんです。ま、付けたし、付けたしのタコ足のプログ
ラムでもないかぎりあまり起こらないと思いますが。(でも、まれにはあるかもしれません。)
よくあるのは、プログラムミスでファイルをクローズしないでプログラムを終了したり、その他のクラッシュ
でプログラムが異常終了して、ファイルをクローズしないまま、プログラムが終了してしまうケースです。
この場合、ファイルはオープンのままなので、もう一度同じプログラムを再起動して、そのファイルを
オープンしようとしても、”そのファイルは既に開かれています。”みないなエラーが出てプログラムが
停止してしまいます。
開いたファイルは必ずクローズするように気をつけましょう。またエラー発生時に、on
... error gosub
等でエラートラップに飛ばした時に、場合によってはCLOSEでファイルを全部クローズするような対策
をしておくことも必要かもしれません。
A同時にオープンできるファイルの数には制限があります。(メモリーの大きさによりますが、
通常8個とか16個とかでしょう) 大きなプログラムで多数のファイルを開く必要がある場合は、
不要になったファイルはクローズして、そのファイル用に使っていたファイル番号を開けてやる必要
があります。
Bについては差しあたって知らなくてもプログラミング上困らないと思いますが、知っていて損は
ないでしょう。バッファについてはファイルバッファをご参照下さい。
BASICによって差異がありますが、他の命令文でもファイルのクローズが実施されることがあります。
F-BASIC: RUN, LOAD, END
N88BASIC: RUN, LOAD, END, NEW
4 数値の入出力
ひととおり基本的なところを書いてきましたが、ファイルの入出力も自分でプログラムを記述して
体?に覚えさせないとなかなか身につかないので、いろいろ試してみてください。
10°毎のSIN,COSの値をファイルに書き込み、それを読み出して表示するプログラムです。
特に説明が必要な部分はないと思います。
10 '****ファイルへの書き込み**** 20 close 30 dim s(18), c(18) 40 open "sin-cos.txt" for output as #1 50 for d=0 to 180 step 10 60 s(n)=sin(3.14/180*d): c(n)=cos(3.14/180*d) 70 print #1, s(n),c(n) 80 n=n+1 90 next 100 close #1 110 ' 120 '****ファイル読み込み**** 130 ' 140 open "sin-cos.txt" for input as #1 150 for i=0 to 18 160 input #1, a,b 170 print "sin ";a,"cos ";b 180 next 190 close #1 |
実行例