コラム
2025/06/25
プログラミングについて 第113回目
『テキストファイルを読み込む その1』
今回からはテキストファイルを読み込むことについて考えていきます。プログラムに避けて通れないものがテキストファイルです。単なる文書のものもあれば、フォーマットの決まったデータであることもあります。またそのフォーマットも様々でそれらの読み込み方も書き込み方も一様に行なうという訳にはなかなかいかないのが現実です。すべてのフォーマットのテキストファイルを例にあげることは不可能ですので、いくつかの例をあげて考えていきましょう。
最初は単純に文書の場合を考えてみましょう。例えば、
Information in this document is subject to change without notice.
The names of companies, products, people, characters and/or data mentioned
herein are fictitious and are in no way intended to represent any real
individual, company, product or event, unless otherwise noted.
といった文書を単純に読み込むというのであれば、多分プログラムを書くのはそれほど難しいものではないと思います。
#include <stdio.h>
main()
{
FILE *fp;
char data[100];
fp=fopen("text.txt","r");
while(fgets(data,sizeof(data),fp))printf("%s",data);
fclose(fp);
}
といったように書くことでファイルから読み込み、標準出力に出力することができます。文書の1行の文字数が100バイト未満という仮定で行単位に読み込んでいます。
もし1行の最大バイト数が不明のときには、
#include <stdio.h>
main()
{
FILE *fp;
int c;
fp=fopen("text.txt","r");
while((c=fgetc(fp))!=EOF)printf("%c",c);
fclose(fp);
}
と1文字ずつ読み込んで標準出力にも1文字ずつ出力します。それぞれの単語を1行に表示するには、
#include <stdio.h>
main()
{
FILE *fp;
char data[20];
fp=fopen("text.txt","r");
while(fscanf(fp,"%s",data)!=EOF)printf("|%s|\n",data);
fclose(fp);
}
とすれば実現できますが、これは1つの単語の最大バイト数が20バイト未満であるという前提です。もし、1つの単語の最大バイト数が不明なときにはこのように安直にはいきません(しかしながら通常はまず間違いないだろうというくらいの文字配列を用意して同様な方法で読み込みます)。こういうときには1文字ずつ読み込んでいくことになります。
#include <stdio.h>
main()
{
FILE *fp;
int c;
fp=fopen("text.txt","r");
while((c=fgetc(fp))!=EOF){
if(c==' ' || c=='\t')printf("\n");
else printf("%c",c);
}
fclose(fp);
}
とすれば単語ごとに1行に表示されます。ここでは単語の区切りをスペースかタブとしています。ところが、区切りとしてのスペースやタブが複数個連続していたときには何もない行が表示されてしまいますので、
#include <stdio.h>
main()
{
FILE *fp;
int c;
int spc;
fp=fopen("text.txt","r");
spc=0;
while((c=fgetc(fp))!=EOF){
if(c==' ' || c=='\t'){
if(!spc)printf("\n");
spc=1;
}
else{
printf("%c",c);
spc=0;
}
}
fclose(fp);
}
としてスペースやタブが連続しているときでも改行は一回のみとするようにします。
ところが本当はこれでもまだ完璧ではありません。
Information in this document is subject to change without notice.
The names of companies, products, people, characters and/or data mentioned
herein are fictitious and are in no way intended to represent any real
individual, company, product or event, unless otherwise noted.
上の文書のように行の先頭に空白が入っていると何もない行が表示されてしまうのです。これは改行を考慮していないために生じるものですので、
#include <stdio.h>
main()
{
FILE *fp;
int c;
int spc;
fp=fopen("text.txt","r");
spc=0;
while((c=fgetc(fp))!=EOF){
if(c==' ' || c=='\t' || c=='\n'){
if(!spc)printf("\n");
spc=1;
}
else{
printf("%c",c);
spc=0;
}
}
fclose(fp);
}
と空白文字の判定に '\n' も加えておかなければなりません。
ここまではファイルから読み込んだ文字を標準出力に出力するだけでしたので、結構簡単に記述することが可能でしたが、単語をソートして表示するといったプログラムの場合には、一度メモリー上に単語を記憶しておかなければなりません。そこで次のようにします。
#include <stdio.h>
#include <malloc.h>
/* 単語リスト */
struct word_list_strct{
char *word;
struct word_list_strct *next;
};
struct word_list_strct *word_list=NULL;
/* 読み込み中の単語 */
char *word=NULL;
int lword=0;
main()
{
FILE *fp;
int c;
int spc;
fp=fopen("z.z","r");
spc=0;
while((c=fgetc(fp))!=EOF){
if(c==' ' || c=='\t' || c=='\n'){
if(!spc)term_char(); /* 単語の終了 */
spc=1;
}
else{
add_char(c); /* 単語に文字を追加 */
spc=0;
}
}
fclose(fp);
}
add_char(c)
int c;
{
/* それまで確保していたメモリー
サイズ+1のメモリーを再確保
し、単語の最後に1文字を追加 */
word=realloc(word,lword+1);
word[lword++]=c;
}
term_char()
{
struct word_list_strct *w;
/* それまで確保していたメモリー
サイズ+1のメモリーを再確保
し、単語の最後にヌルを追加 */
word=realloc(word,lword+1);
word[lword++]='\0';
/* 単語リスト構造体用のメモリー
を確保し、読み込み中の単語への
ポインタを記憶し、単語リストに
つなぐ */
w=(struct word_list_strct *)malloc(sizeof(struct word_list_strct));
w->word=word;
w->next=word_list;
word_list=w;
/* 読み込み中の単語用の変数を
初期化する */
word=NULL;
lword=0;
}
単語リストは単語の出現順と逆の順で作成されますが、ソートすることを前提とするのならば、これでも問題はありません。またこのプログラム例はソートと表示部分は本題とは違いますので省略します。メモリーを確保する部分は realloc を使って1文字ごとに行なっていますので、文書サイズが大きいと実行速度が上がらない可能性がありますので、そういうときには読み込み中の単語用のメモリーはそれまで読み込んだ単語の最大長を越えるときにのみ再確保するようにし、単語リストに追加するとき改めてそれ用のメモリーを確保するようにします。
それではまた次回。
第113回 プログラミングについて『テキストファイルを読み込む その1』

プログラミングについて 第113回目
『テキストファイルを読み込む その1』
今回からはテキストファイルを読み込むことについて考えていきます。プログラムに避けて通れないものがテキストファイルです。単なる文書のものもあれば、フォーマットの決まったデータであることもあります。またそのフォーマットも様々でそれらの読み込み方も書き込み方も一様に行なうという訳にはなかなかいかないのが現実です。すべてのフォーマットのテキストファイルを例にあげることは不可能ですので、いくつかの例をあげて考えていきましょう。
最初は単純に文書の場合を考えてみましょう。例えば、
Information in this document is subject to change without notice.
The names of companies, products, people, characters and/or data mentioned
herein are fictitious and are in no way intended to represent any real
individual, company, product or event, unless otherwise noted.
といった文書を単純に読み込むというのであれば、多分プログラムを書くのはそれほど難しいものではないと思います。
#include <stdio.h>
main()
{
FILE *fp;
char data[100];
fp=fopen("text.txt","r");
while(fgets(data,sizeof(data),fp))printf("%s",data);
fclose(fp);
}
といったように書くことでファイルから読み込み、標準出力に出力することができます。文書の1行の文字数が100バイト未満という仮定で行単位に読み込んでいます。
もし1行の最大バイト数が不明のときには、
#include <stdio.h>
main()
{
FILE *fp;
int c;
fp=fopen("text.txt","r");
while((c=fgetc(fp))!=EOF)printf("%c",c);
fclose(fp);
}
と1文字ずつ読み込んで標準出力にも1文字ずつ出力します。それぞれの単語を1行に表示するには、
#include <stdio.h>
main()
{
FILE *fp;
char data[20];
fp=fopen("text.txt","r");
while(fscanf(fp,"%s",data)!=EOF)printf("|%s|\n",data);
fclose(fp);
}
とすれば実現できますが、これは1つの単語の最大バイト数が20バイト未満であるという前提です。もし、1つの単語の最大バイト数が不明なときにはこのように安直にはいきません(しかしながら通常はまず間違いないだろうというくらいの文字配列を用意して同様な方法で読み込みます)。こういうときには1文字ずつ読み込んでいくことになります。
#include <stdio.h>
main()
{
FILE *fp;
int c;
fp=fopen("text.txt","r");
while((c=fgetc(fp))!=EOF){
if(c==' ' || c=='\t')printf("\n");
else printf("%c",c);
}
fclose(fp);
}
とすれば単語ごとに1行に表示されます。ここでは単語の区切りをスペースかタブとしています。ところが、区切りとしてのスペースやタブが複数個連続していたときには何もない行が表示されてしまいますので、
#include <stdio.h>
main()
{
FILE *fp;
int c;
int spc;
fp=fopen("text.txt","r");
spc=0;
while((c=fgetc(fp))!=EOF){
if(c==' ' || c=='\t'){
if(!spc)printf("\n");
spc=1;
}
else{
printf("%c",c);
spc=0;
}
}
fclose(fp);
}
としてスペースやタブが連続しているときでも改行は一回のみとするようにします。
ところが本当はこれでもまだ完璧ではありません。
Information in this document is subject to change without notice.
The names of companies, products, people, characters and/or data mentioned
herein are fictitious and are in no way intended to represent any real
individual, company, product or event, unless otherwise noted.
上の文書のように行の先頭に空白が入っていると何もない行が表示されてしまうのです。これは改行を考慮していないために生じるものですので、
#include <stdio.h>
main()
{
FILE *fp;
int c;
int spc;
fp=fopen("text.txt","r");
spc=0;
while((c=fgetc(fp))!=EOF){
if(c==' ' || c=='\t' || c=='\n'){
if(!spc)printf("\n");
spc=1;
}
else{
printf("%c",c);
spc=0;
}
}
fclose(fp);
}
と空白文字の判定に '\n' も加えておかなければなりません。
ここまではファイルから読み込んだ文字を標準出力に出力するだけでしたので、結構簡単に記述することが可能でしたが、単語をソートして表示するといったプログラムの場合には、一度メモリー上に単語を記憶しておかなければなりません。そこで次のようにします。
#include <stdio.h>
#include <malloc.h>
/* 単語リスト */
struct word_list_strct{
char *word;
struct word_list_strct *next;
};
struct word_list_strct *word_list=NULL;
/* 読み込み中の単語 */
char *word=NULL;
int lword=0;
main()
{
FILE *fp;
int c;
int spc;
fp=fopen("z.z","r");
spc=0;
while((c=fgetc(fp))!=EOF){
if(c==' ' || c=='\t' || c=='\n'){
if(!spc)term_char(); /* 単語の終了 */
spc=1;
}
else{
add_char(c); /* 単語に文字を追加 */
spc=0;
}
}
fclose(fp);
}
add_char(c)
int c;
{
/* それまで確保していたメモリー
サイズ+1のメモリーを再確保
し、単語の最後に1文字を追加 */
word=realloc(word,lword+1);
word[lword++]=c;
}
term_char()
{
struct word_list_strct *w;
/* それまで確保していたメモリー
サイズ+1のメモリーを再確保
し、単語の最後にヌルを追加 */
word=realloc(word,lword+1);
word[lword++]='\0';
/* 単語リスト構造体用のメモリー
を確保し、読み込み中の単語への
ポインタを記憶し、単語リストに
つなぐ */
w=(struct word_list_strct *)malloc(sizeof(struct word_list_strct));
w->word=word;
w->next=word_list;
word_list=w;
/* 読み込み中の単語用の変数を
初期化する */
word=NULL;
lword=0;
}
単語リストは単語の出現順と逆の順で作成されますが、ソートすることを前提とするのならば、これでも問題はありません。またこのプログラム例はソートと表示部分は本題とは違いますので省略します。メモリーを確保する部分は realloc を使って1文字ごとに行なっていますので、文書サイズが大きいと実行速度が上がらない可能性がありますので、そういうときには読み込み中の単語用のメモリーはそれまで読み込んだ単語の最大長を越えるときにのみ再確保するようにし、単語リストに追加するとき改めてそれ用のメモリーを確保するようにします。
それではまた次回。