コラム
2025/07/31
プログラミングについて 第116回目
『テキストファイルを読み込む その4』
テキストファイルを読み込むということで、これまで3回に渡ってお話ししてきました。少しは参考になったでしょうか。
とにもかくにも世の中には様々なフォーマットのデータがあるもので、そのフォーマットに合わせてプログラムを作らなければなりません。また読み込んだデータをどのようにプログラムの中で保持するかというのもデータのフォーマットにも左右され、またどのような処理をするのかによって工夫しなければなりません。
テキストファイルに限ったことではありませんが、できる限りファイルを読み込む部分と読み込んだデータを処理する部分は独立させておいた方がプログラムが作りやすくなります。要するにファイルからデータを読み込む部分はそれに専念して、読み込んでくれと依頼する方が処理をしやすい状態にしてデータを返してあげるようにするのです。
単純なフォーマットのデータの場合は適当にやってしまいますが、ちょっと複雑なフォーマットのときにはファイルからどのようにデータを読み込んで、どのように依頼側に渡すのかについて頭を悩ますことが少なくありません。この方法いかんによってプログラムが書きやすくなるか否かが大きく左右されるからです。最悪の場合には完成しないなんてことにもなりかねません。
例えば次のデータは機械系CAD(AutoCAD)のDXFフォーマットです。
0 ←データの種類
SECTION ←データ
2 ←データの種類
HEADER ←データ
9 ←データの種類
$ACADVER ←データ
1 ←データの種類
AC1006 ←データ
9 ←データの種類
$INSBASE ←データ
10 ←データの種類
0.0 ←データ
20 ←データの種類
0.0 ←データ
30 ←データの種類
0.0 ←データ
このフォーマットは、最初にデータの種類を示す番号、次にそのデータという順番で表現されるフォーマットです。
このデータを読み込もうとしたときに読み込みを依頼する側がデータを読み込む側に、
(1)次のデータの種類はなんですか。
(2)その内容を教えてください。
という順番で処理をしていくのか、
(1)次のデータの種類と内容を教えて下さい。
と処理をするのかでデータのロード部分のプログラムの書き方が大きく変ってきます。
次のデータはプリント基板CAD(CR5000)で使用されるテキストファイルです。
$MDF {
CMP_HEADR 8647 : COMP : 14 : SMD ;
EDIT_AREA_SIZE 20.0000 : 15.0000 : 10.0000 : 7.5000 ;
CMP_ATTRIBUTE 1.9000 : : POR : 0 : "P" : "IC" : 0.0000 : 0.0000 : 4 ;
INSERTION 0 : 1.2700 : 0.0000 ;
SYM_POSITION 0.0000 , 0.7000 : 0.0000 , 0.0000 : 1.0000 , 0.0000
: 0.0000 , 0.0000 ;
CMP {
TERM ( 1 ) : : 4 : LINE : -3.8100 , -3.0000 : ;
TERM ( 2 ) : : 4 : LINE : -2.5400 , -3.0000 : ;
TERM ( 3 ) : : 4 : LINE : -1.2700 , -3.0000 : ;
LAY ( 1 ) : LND : : 1 : 5 : -3.7500 , -5.0000 ;
LAY ( 1 ) : LIN : : SOLID : 0.5000 : 0.5000 : 1.0000 : 5 ;
1 : : : 19 : -5.0000 , -1.8000 ;
2 : : : 19 : 5.0000 , -1.8000 ;
3 : : : 19 : 5.0000 , 1.8000 ;
4 : : : 19 : -5.0000 , 1.8000 ;
5 : : : 19 : -5.0000 , -1.8000 ;
LAY ( 2 ) : LIN : : SOLID : 0.5000 : 0.5000 : 1.0000 : 2 ;
1 : : : 28 : -3.8100 , -2.4250 ;
2 : : : 28 : -3.8100 , -3.5750 ;
}
}
このフォーマットは、
キーワード{
レコード
キーワード{
レコード
}
}
という形式で { } が入れ子になる構造です。また1つのレコードは、各データはコロンで区切られ、セミコロンで終端します。1つのデータが複数個の要素を持つときにはそれらはコンマで区切られます。文字列はダブルクォーテーションで囲まれ、文字列以外の空白文字は基本的には意味を持ちません。また1つのレコードは途中で改行しても構いません。
このようなフォーマットのファイルを読み込みを依頼する側がデータを読み込む側に対して依頼する方法も幾通りの方法があります。
最初に考えられるのは、
(1)次のキーワードまたはレコードを教えて下さい。
という方法で、この依頼を受けたら読み込み側は最初に、
$MDF
または
$MDF {
を返し、次に、
{
または
CMP_HEADR 8647 : COMP : 14 : SMD ;
を返すようにします。ここで問題は文字列以外の空白は基本的には意味を持たないので、上のレコードの空白を削除すると、
CMP_HEADR8647:COMP:14:SMD;
となり、CMP_HEADR と 8647 がつながってしまい読み込みを依頼した側が解釈をするのが面倒になってしまいます。こういうときには先頭と末尾の空白、区切り記号の前後の空白のみを無視するようにして、
CMP_HEADR 8647:COMP:14:SMD;
と返した方が親切かも知れません。ですので最初のものは { も含めて返す場合には、$MDF { と返す代わりに $MDF{ と返すことになります。
次に考えられるのは、
(1)次のキーワードまたはデータを教えて下さい。
と依頼する方法です(キーワードには { も含め、余分な空白は無視することにします)。
最初は、
$MDF{
を返し、次に返すのは、
CMP_HEADR 8647: - 1
CMP_HEADR 8647 - 2
CMP_HEADR - 3
のいずれかになります。どれがいいのかは判断に迷うところです。
1の方法では読み込みを依頼した側が1つのデータの内容を区切り記号まで解析する必要がありますが、区切り記号によって1つのレコードが終了するのか否かを判断することができます。
2の方法では区切り記号が含まれていませんので、なんらかの方法で1つのレコードが終わったということを教えてあげなければなりません。
3の方法では読み込み側は1つのデータが終了したか否か、1つのレコードが終了したか否かを教えてあげなければなりません。
どの方法もそれなりに長所と短所がありますので頭の痛いところです。
上記の他にも読み込む方法は考えられますが、どの方法をとるにしてもプログラムのロード部が記述しやすい方法を選ぶべきです。
どのようなプログラムを作るにしても実現方法は1つではありません。またどの場面でも選択子は多岐に渡りますのでその中のどの手法を採用するのかはなかなか理屈では説明のできないところです。多分に経験が物を言うとしか言えないかも知れません。テキストファイルを読み込むという部分に関してもこれは同じことで、上記のようにどちらでもいいようなことで迷ってしまうことが少なくありません。私がアドバイスできるとしたらやはり、とにかく色々なプログラムをたくさん作ってみることしか言えないかも知れません。
今回はちょっと短いですが、また次回。
第116回 プログラミングについて『テキストファイルを読み込む その4』

プログラミングについて 第116回目
『テキストファイルを読み込む その4』
テキストファイルを読み込むということで、これまで3回に渡ってお話ししてきました。少しは参考になったでしょうか。
とにもかくにも世の中には様々なフォーマットのデータがあるもので、そのフォーマットに合わせてプログラムを作らなければなりません。また読み込んだデータをどのようにプログラムの中で保持するかというのもデータのフォーマットにも左右され、またどのような処理をするのかによって工夫しなければなりません。
テキストファイルに限ったことではありませんが、できる限りファイルを読み込む部分と読み込んだデータを処理する部分は独立させておいた方がプログラムが作りやすくなります。要するにファイルからデータを読み込む部分はそれに専念して、読み込んでくれと依頼する方が処理をしやすい状態にしてデータを返してあげるようにするのです。
単純なフォーマットのデータの場合は適当にやってしまいますが、ちょっと複雑なフォーマットのときにはファイルからどのようにデータを読み込んで、どのように依頼側に渡すのかについて頭を悩ますことが少なくありません。この方法いかんによってプログラムが書きやすくなるか否かが大きく左右されるからです。最悪の場合には完成しないなんてことにもなりかねません。
例えば次のデータは機械系CAD(AutoCAD)のDXFフォーマットです。
0 ←データの種類
SECTION ←データ
2 ←データの種類
HEADER ←データ
9 ←データの種類
$ACADVER ←データ
1 ←データの種類
AC1006 ←データ
9 ←データの種類
$INSBASE ←データ
10 ←データの種類
0.0 ←データ
20 ←データの種類
0.0 ←データ
30 ←データの種類
0.0 ←データ
このフォーマットは、最初にデータの種類を示す番号、次にそのデータという順番で表現されるフォーマットです。
このデータを読み込もうとしたときに読み込みを依頼する側がデータを読み込む側に、
(1)次のデータの種類はなんですか。
(2)その内容を教えてください。
という順番で処理をしていくのか、
(1)次のデータの種類と内容を教えて下さい。
と処理をするのかでデータのロード部分のプログラムの書き方が大きく変ってきます。
次のデータはプリント基板CAD(CR5000)で使用されるテキストファイルです。
$MDF {
CMP_HEADR 8647 : COMP : 14 : SMD ;
EDIT_AREA_SIZE 20.0000 : 15.0000 : 10.0000 : 7.5000 ;
CMP_ATTRIBUTE 1.9000 : : POR : 0 : "P" : "IC" : 0.0000 : 0.0000 : 4 ;
INSERTION 0 : 1.2700 : 0.0000 ;
SYM_POSITION 0.0000 , 0.7000 : 0.0000 , 0.0000 : 1.0000 , 0.0000
: 0.0000 , 0.0000 ;
CMP {
TERM ( 1 ) : : 4 : LINE : -3.8100 , -3.0000 : ;
TERM ( 2 ) : : 4 : LINE : -2.5400 , -3.0000 : ;
TERM ( 3 ) : : 4 : LINE : -1.2700 , -3.0000 : ;
LAY ( 1 ) : LND : : 1 : 5 : -3.7500 , -5.0000 ;
LAY ( 1 ) : LIN : : SOLID : 0.5000 : 0.5000 : 1.0000 : 5 ;
1 : : : 19 : -5.0000 , -1.8000 ;
2 : : : 19 : 5.0000 , -1.8000 ;
3 : : : 19 : 5.0000 , 1.8000 ;
4 : : : 19 : -5.0000 , 1.8000 ;
5 : : : 19 : -5.0000 , -1.8000 ;
LAY ( 2 ) : LIN : : SOLID : 0.5000 : 0.5000 : 1.0000 : 2 ;
1 : : : 28 : -3.8100 , -2.4250 ;
2 : : : 28 : -3.8100 , -3.5750 ;
}
}
このフォーマットは、
キーワード{
レコード
キーワード{
レコード
}
}
という形式で { } が入れ子になる構造です。また1つのレコードは、各データはコロンで区切られ、セミコロンで終端します。1つのデータが複数個の要素を持つときにはそれらはコンマで区切られます。文字列はダブルクォーテーションで囲まれ、文字列以外の空白文字は基本的には意味を持ちません。また1つのレコードは途中で改行しても構いません。
このようなフォーマットのファイルを読み込みを依頼する側がデータを読み込む側に対して依頼する方法も幾通りの方法があります。
最初に考えられるのは、
(1)次のキーワードまたはレコードを教えて下さい。
という方法で、この依頼を受けたら読み込み側は最初に、
$MDF
または
$MDF {
を返し、次に、
{
または
CMP_HEADR 8647 : COMP : 14 : SMD ;
を返すようにします。ここで問題は文字列以外の空白は基本的には意味を持たないので、上のレコードの空白を削除すると、
CMP_HEADR8647:COMP:14:SMD;
となり、CMP_HEADR と 8647 がつながってしまい読み込みを依頼した側が解釈をするのが面倒になってしまいます。こういうときには先頭と末尾の空白、区切り記号の前後の空白のみを無視するようにして、
CMP_HEADR 8647:COMP:14:SMD;
と返した方が親切かも知れません。ですので最初のものは { も含めて返す場合には、$MDF { と返す代わりに $MDF{ と返すことになります。
次に考えられるのは、
(1)次のキーワードまたはデータを教えて下さい。
と依頼する方法です(キーワードには { も含め、余分な空白は無視することにします)。
最初は、
$MDF{
を返し、次に返すのは、
CMP_HEADR 8647: - 1
CMP_HEADR 8647 - 2
CMP_HEADR - 3
のいずれかになります。どれがいいのかは判断に迷うところです。
1の方法では読み込みを依頼した側が1つのデータの内容を区切り記号まで解析する必要がありますが、区切り記号によって1つのレコードが終了するのか否かを判断することができます。
2の方法では区切り記号が含まれていませんので、なんらかの方法で1つのレコードが終わったということを教えてあげなければなりません。
3の方法では読み込み側は1つのデータが終了したか否か、1つのレコードが終了したか否かを教えてあげなければなりません。
どの方法もそれなりに長所と短所がありますので頭の痛いところです。
上記の他にも読み込む方法は考えられますが、どの方法をとるにしてもプログラムのロード部が記述しやすい方法を選ぶべきです。
どのようなプログラムを作るにしても実現方法は1つではありません。またどの場面でも選択子は多岐に渡りますのでその中のどの手法を採用するのかはなかなか理屈では説明のできないところです。多分に経験が物を言うとしか言えないかも知れません。テキストファイルを読み込むという部分に関してもこれは同じことで、上記のようにどちらでもいいようなことで迷ってしまうことが少なくありません。私がアドバイスできるとしたらやはり、とにかく色々なプログラムをたくさん作ってみることしか言えないかも知れません。
今回はちょっと短いですが、また次回。