プリント基板設計・シミュレーション

TOP > アポロレポート > コラム > 第123回 プログラミングについて『作っておくと便利なちょっとしたプログラム その7』
コラム
2025/12/25

第123回 プログラミングについて『作っておくと便利なちょっとしたプログラム その7』

アポロレポート

           プログラミングについて 第123回目

       『作っておくと便利なちょっとしたプログラム その7』

今回は前回の続きです。まずは前回までのプログラムです。

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <malloc.h>

 #define NEW(type,element) (type *)malloc(sizeof(type)*(element))
 #define DELETE(addr)  free(addr)

 #define PROC_MODE_EXPAND  1
 #define PROC_MODE_COMPRESS 2
 #define PROC_MODE_HELP   3
 int proc_mode=PROC_MODE_EXPAND;

 int ignore_terminal_space=0;

 #define LEFT_ADJUST    1
 #define RIGHT_ADJUST    2
 int display_mode=LEFT_ADJUST;

 int display_add_space=0;

 char text_enclose_char='"';
 char text_escape_char='\\';

 char *fname=NULL;
 FILE *fp=stdin;

 char *new_strcat(char *string1,char *string2);

 void main(argc,argv)
 int  argc;
 char *argv[];
 {
  if(analize_argment(argc,argv)== -1)goto end;

  if(proc_mode==PROC_MODE_HELP){
  }
  else{
   if(proc_mode==PROC_MODE_COMPRESS){
    ignore_terminal_space=1;
      display_add_space=0;
         display_mode=LEFT_ADJUST;
   }

   if(open_source()== -1)goto end;

   close_source();
  }

 end:
  exit(0);
 }

 analize_argment(argc,argv)
 int  argc;
 char *argv[];
 {
  int i;

  for(i=1;i<argc;i++){
      if(stricmp(argv[i],"-c")==0
      || stricmp(argv[i],"/c")==0)proc_mode=PROC_MODE_COMPRESS;
   else if(stricmp(argv[i],"-e")==0
      || stricmp(argv[i],"/e")==0)proc_mode=PROC_MODE_EXPAND;
   else if(stricmp(argv[i],"-?")==0
      || stricmp(argv[i],"/?")==0){
    proc_mode=PROC_MODE_HELP;
    return 1;
   }
   else if(stricmp(argv[i],"-i")==0
      || stricmp(argv[i],"/i")==0)ignore_terminal_space=1;
   else if(stricmp(argv[i],"-l")==0
      || stricmp(argv[i],"/l")==0)display_mode=LEFT_ADJUST;
   else if(stricmp(argv[i],"-r")==0
      || stricmp(argv[i],"/r")==0)display_mode=RIGHT_ADJUST;
   else if(stricmp(argv[i],"-t1")==0
      || stricmp(argv[i],"/t1")==0){
    text_enclose_char='"' ;
     text_escape_char='\\';
   }
   else if(stricmp(argv[i],"-t2")==0
      || stricmp(argv[i],"/t2")==0){
    text_enclose_char='"';
     text_escape_char='"';
   }
   else if(stricmp(argv[i],"-s")==0
      || stricmp(argv[i],"/s")==0)display_add_space=1;
   else{
    for(;i<argc;i++){
     if(fname && fname[0])fname=new_strcat(fname," ");
     fname=new_strcat(fname,argv[i]);
    }
   }
  }

  return 1;
 }

 char *new_strcat(char *string1,char *string2)
 {
  if(string1==NULL){
   string1=NEW(char,strlen(string2)+1);
   strcpy(string1,string2);
  }
  else{
   int l;

   l=strlen(string1)+strlen(string2)+1;

   if(l>_msize(string1))string1=realloc(string1,l);

   strcat(string1,string2);
  }

  return string1;
 }

 open_source()
 {
  if(fname){
   fp=fopen(fname,"r");
   if(fp==NULL){
    printf("入力ファイル\n");
    printf("File : %s\n",fname);
    printf("のオープン時にエラーが発生しました。\n");
    printf("%s\n",strerror(errno));
    return -1;
   }
  }

  return 1;
 }

 close_source()
 {
  if(fname)fclose(fp);

  return 1;
 }

拡張表示を行うには、各項目の最大の長さが分からないと表示できませんので、データファイルを一度全部を読み込んでから表示することになります。ファイルの読み込みを2度行うことにして、1度目を各項目の最大の長さを取得する目的に行い、2度目を表示するという方法でも構いませんが、読み込みは一度だけとしメモリー内にテキストを記憶することにします。
 プログラム内での表現は、

 struct data_text_strct{
  char *text;
  struct data_text_strct *next;
 };

 struct data_head_strct{
  struct data_text_strct *data_text;
  struct data_head_strct *next;
 };
 struct data_head_strct *data_head_top=NULL;

というように単純なリスト形式にします。単純とはいっても、data_head_strct は行のリストで、data_text_strct は各行の項目のリストです。data_head_strct はその行の項目のリストの先頭を指し示すような構造です。data_head_top は行のリストの先頭を指し示すようにするのですが、実際にファイルをロードする際には、この変数しかないときには無駄にリストの最後を探さなければならないので、

 struct data_head_strct *data_head_end=NULL;
 struct data_text_strct *data_text_end=NULL;

の2つの変数も用意します。data_head_end は現在の行のリストの最終要素を指し示し、data_text_end は現在の項目の最終要素を指し示す目的に使用します。これらの宣言と定義は関数外であらかじめ行っておきます。そしてメイン関数のファイルのオープンとクローズの間に関数 load_source の呼び出しを追加します。

 if(open_source()== -1)goto end;

 if(load_source()== -1)goto end;

 close_source();

関数 load_source は殆ど以前に作った uline の process_uline と同じです。

 load_source()
 {
  char data[READ_BUF_SIZE+1];
  int ldata;

  char *text;
  int is,ie;

  text=NULL;

  while(ldata=fread(data,1,READ_BUF_SIZE,fp)){
   data[ldata]='\n';

   for(is=ie=0;ie<=ldata;ie++){
    if(data[ie]!='\n')continue;

    data[ie]='\0';
    text=new_strcat(text,data+is);
    is=ie+1;

    if(ie==ldata)break;

    text=new_strcat(text,",");
    add_source(text);

    text[0]='\0';
   }
  }

  DELETE(text);

  return 1;
 }

この関数の、

 text=new_strcat(text,",");
 add_source(text);

の部分がちょっと説明が必要かも知れません。関数 add_source の引数には1行分の文字列が入ることになりますので、その1行分を上で説明した構造体のリストに追加することを行うようにしますが、 text=new_strcat(text,","); の意味が分からないと思います。これは関数 add_source の記述方法にもよるのですが、それを簡単にするための門番を最後に追加しているのです。
 関数 add_source は文字列の最後に到達するまで順に処理していきますが、各項目はコンマの位置を頼りにして区切っていきます。このとき、何も項目がない行や、最後の項目ではコンマがないので特別な処理を行なわなければならないことになります。それではプログラムが面倒になってしまいますので、門番としてのコンマを最後に追加している訳です。

 例えば、

abcde,12345

のときに、

abcde,12345,

とコンマを最後に追加すると、必ず文字列の最後はコンマになります(当たり前ですが)。こうするとプログラムはコンマに出会ったときに項目を追加すれば良いわけです。

次に関数 add_source です。

 add_source(data)
 char *data;
 {
  char *item;
               /* 行(data_head_strct)を追加します。*/
  add_new_data_head();
               /* 各項目を追加する前に項目の文字列へのポインタ
                 item を初期化しておきます。*/
  item=new_strcat(NULL,"");
               /* 行の終わりに到達するまで、各項目の文字列を
                 取得し、行に項目を追加していきます。*/
  while(data[0]){
               /* 次の項目の文字列を取り出します。取り出した後は
                 data からその項目を削除します。*/
   item=get_next_item(data,item);
               /* 項目(data_text_strct)を追加します。*/
   add_new_data_text(item);
  }
               /* 使い終わったメモリーを解放します。*/
  DELETE(item);

  return 1;
 }

難しい内容ではないので、これ以上説明の必要はないでしょう。

関数 add_new_data_head は、

 add_new_data_head()
 {
  struct data_head_strct *dhc;
  dhc=NEW(struct data_head_strct,1); /* メモリーを確保 */
  dhc->data_text=NULL;        /* 項目へのポインタをヌル */
  dhc->next=NULL;          /* 次の行へのポインタをヌル */

                    /* data_head_end がヌルのときにはまだ
                     行データが何もないので、data_head_top
                     につなぎ、そうでないときには、現在の
                     最終行につなぐ */
  if(data_head_end==NULL)data_head_top   =dhc;
  else          data_head_end->next=dhc;

                    /* 現在の最終行へのポインタを更新する */
  data_head_end=dhc;

                    /* 現在の最後の項目へのポインタをヌルに
                     する。これをヌルにしておかないと項目
                     を前の行の項目の最後に追加してしまい
                     ます */
  data_text_end=NULL;

  return 1;
 }

関数 add_new_data_text は、

 add_new_data_text(text)
 char *text;
 {
  struct data_text_strct *dtc;
                    /* メモリーを確保し、項目のテキストを設
                     定、次の項目へのポインタはヌル */
  dtc=NEW(struct data_text_strct,1);
  dtc->text=new_strcat(NULL,text);
  dtc->next=NULL;
                    /* data_text_end がヌルのときには行デー
                     タにはまだ項目がないので、行データの
                     項目につなぎ、そうでないときには現在
                     の項目の最後につなぎます */
  if(data_text_end==NULL)data_head_end->data_text=dtc;
  else          data_text_end->next   =dtc;

                    /* 現在の最後の項目へのポインタを更新 */
  data_text_end=dtc;

  return 1;
 }

です。これも問題はありませんね。長くなってきましたので続きはまた次回にしましょう。それではまた。

そのお悩み、
アポロ技研に話してみませんか?

アポロ技研でワンランク上の物創りへ!
そのお悩み、アポロ技研に話してみませんか?