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

TOP > アポロレポート > コラム > 第125回 プログラミングについて『作っておくと便利なちょっとしたプログラム その9』
コラム
2026/01/21

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

アポロレポート

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

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

 今回も引き続きプログラムを作っていきましょう。今回はテキストファイルの行数表示プログラムです。このプログラムは特に必要という訳ではありませんが、時々ソースプログラムが一体何行あるのかを知りたいときに作っておけばよかったと思うことがあります。プログラムを1つのファイルで作成しているときにはテキストエディタでその行数を見ればいいのですが、複数に分けているときには非常に面倒なものです。何行のプログラムになったのかを知って一人でニヤニヤすることにしか使わないのかも知れませんが、作っておくと使う機会はあるはずです。

もうどんな結果が表示されればいいのかはお分かりと思います。おおよそ次のようにしましょう。

 行数 ファイル名
   :
   :
 総行数

といった結果を表示するようにしましょう。

プログラムの起動方法は、

 linetot [-?] [-s] [-h] [ファイル名 [...]]

の形式にしましょう。

-? のときにはオンラインヘルプを表示します。
-s を指定するとサブディレクトリも検索します。
-h を指定すると非表示属性のファイルやディレクトリも検索します。

 ファイル名にはワイルドカードも使用できるようにします。またファイル名は複数個指定ができ、ファイル名を指定しなかったときには標準入力からの読み込みにします。

どんどん作っていきましょう。導入部はこれまでのプログラムと殆ど同じですのでさっと流していきましょう。

 #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_DISPLAY 1
 #define PROC_MODE_HELP  2
 int proc_mode=PROC_MODE_DISPLAY;

 int with_hide   =0;
 int search_sub_directory=0;

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

 struct file_name_strct{
  char *fname;
  struct file_name_strct *next;
 };
 struct file_name_strct *file_name_top=NULL;
 struct file_name_strct *file_name_end=NULL;

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

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

 end:
  exit(0);
 }

この部分で、

 #define PROC_MODE_DISPLAY 1
 #define PROC_MODE_HELP  2
 int proc_mode=PROC_MODE_DISPLAY;

は行数を表示するのかヘルプを表示するのかの処理モードです。

 int with_hide      =0;
 int search_sub_directory=0;

の with_hide は非表示のファイルやディレクトリも検索するか否かを示す変数で、真のときには検索を行い、偽のときには行ないません。search_sub_directory はサブディレクトリも検索するか否かを示す変数で、真のときには検索を行い、偽のときには行ないません。

 struct file_name_strct{
  char *fname;
  struct file_name_strct *next;
 };
 struct file_name_strct *file_name_top=NULL;
 struct file_name_strct *file_name_end=NULL;

は、パラメータで指定されたファイル名を入れておく構造体で、単純なリスト形式のデータ構造です。file_name_top はリストの先頭の、file_name_end はリストの末端のポインタです。

 プログラムのパラメータを解析する関数 analize_argment は、

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

  for(i=1;i<argc;i++){
   if(stricmp(argv[i],"-?")==0
   || stricmp(argv[i],"/?")==0){
    proc_mode=PROC_MODE_HELP;
    return 1;
   }
   else if(stricmp(argv[i],"-s")==0
      || stricmp(argv[i],"/s")==0)search_sub_directory=1;
   else if(stricmp(argv[i],"-h")==0
      || stricmp(argv[i],"/h")==0)with_hide=1;
   else{
    struct file_name_strct *fnc;

    fnc=NEW(struct file_name_strct,1);
    fnc->fname=new_strcat(NULL,argv[i]);
    fnc->next =NULL;

    if(file_name_end==NULL)file_name_top   =fnc;
    else          file_name_end->next=fnc;

    file_name_end=fnc;
   }
  }

  return 1;
 }

です。前回までのプログラムではパラメータとして指定することのできるファイル名は1つだけでしたので、オプションの後のパラメータすべてを連結しましたが、このプログラムでは複数個のファイルを指定できることにしていますので、それぞれをリストに入れていくことにします。DOS用のプログラムということを前提にしていますので、32ビット版のWindowsのDOSプロンプト内でパス名やファイル名に空白文字が含まれているときには、ダブルクォーテーションで囲んでファイル名を指定するようにします。関数 new_strcat もこれまでのものと同じものを使用します。

次にメイン関数の analize_argment の呼び出しの後に、

 if(proc_mode==PROC_MODE_HELP){
  /*
  display_help();
  */
 }
 else{
  if(display_total_lines()== -1)goto end;
 }

を追加します。ヘルプの表示機能は最後に付けることにしますので、ここではコメントにしておきます。

関数 display_total_lines は総行数を表示するもので、行数表示機能のメインになる関数です。まず標準入力からの読み込みに対する行数の表示を行なうことにします。

その前に総行数を保持する変数を用意しておかなければなりませんので、

unsigned int grand_total_lines=0;

をメイン関数の前にグローバルに定義しておきましょう。unsigned int 型にしてあるのは、総行数が大きくなったときの保険のようなもので、最上位ビットを符号ビットではなく、値として使用するためです。

 display_total_lines()
 {
  if(file_name_top==NULL){
   printf("\n===== 標準入力 =====\n\n");

   if(display_file_total_lines()== -1)return -1;

   printf("\n==================\n");
   printf("\n合計 %10u 行\n",grand_total_lines);
  }

  return 1;
 }

ファイル名リストの先頭のポインタ file_name_top がヌルのときにはファイル名の指定がなかったときですので、標準入力からの読み込みになります。呼び出している関数 display_file_total_lines は、1つの入力単位(ファイルまたは標準入力)の行数を表示することと、グローバルに定義した grand_total_lines にその行数を加算する機能を持つものにします。ここまでは全然難しくないと思います。

関数 display_file_total_lines は、

 display_file_total_lines()
 {
  unsigned int lines;
  int c;

  lines=0;

  while((c=fgetc(fp))!=EOF){
   if(c=='\n')lines++;
  }

  if(fname)printf(" %7d 行 %s\n",lines,fname);
  else   printf(" %7d 行\n"  ,lines);

  grand_total_lines+=lines;

  return 1;
 }

と1文字ずつ読み込んでもいいのですが、前回までの説明に従って読み込み回数を減らすために、まとめて読み込むことにします。

 display_file_total_lines()
 {
  unsigned int lines;

  char data[READ_BUF_SIZE];
  int ldata,i;

  lines=0;

  while(ldata=fread(data,1,READ_BUF_SIZE,fp)){
   for(i=0;i<ldata;i++){
    if(data[i]=='\n')lines++;
   }
  }

  if(fname)printf(" %7d 行 %s\n",lines,fname);
  else   printf(" %7d 行\n"  ,lines   );

  grand_total_lines+=lines;

  return 1;
 }

 このプログラムでも行数の勘定はラインフィード('\n')の数のみで行なっています。これでも前回までの説明のようにシフトJISの2バイト文字では第1バイト目も第2バイト目もこの値をとることがないため、これで問題なく機能します。
 行数の表示は変数 fname がヌルのときには標準入力からの読み込みですので行数のみとし、それ以外のときには行数とファイル名の表示を行なうようにします。行数の表示後、grand_total_lines にその行数を加算します。これは、標準入力のときには1つの入力単位しか行ないませんので特に必要はないのですが、ファイルの行数を勘定するときには複数ファイルの行数を表示した最後にそれらの総行数も表示することにしますので、ここで加算しておくことにします。
 READ_BUF_SIZE はこれまでと同様に、#define READ_BUF_SIZE 8192 と定義しておきます。

それではここまでの全リストです。

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

 #define READ_BUF_SIZE  8192

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

 #define PROC_MODE_DISPLAY 1
 #define PROC_MODE_HELP  2
 int proc_mode=PROC_MODE_DISPLAY;

 int with_hide   =0;
 int search_sub_directory=0;

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

 struct file_name_strct{
  char *fname;
  struct file_name_strct *next;
 };
 struct file_name_strct *file_name_top=NULL;
 struct file_name_strct *file_name_end=NULL;

 unsigned int grand_total_lines=0;

 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){
   /*
   display_help();
   */
  }
  else{
   if(display_total_lines()== -1)goto end;
  }

 end:
  exit(0);
 }

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

  for(i=1;i<argc;i++){
   if(stricmp(argv[i],"-?")==0
   || stricmp(argv[i],"/?")==0){
    proc_mode=PROC_MODE_HELP;
    return 1;
   }
   else if(stricmp(argv[i],"-s")==0
      || stricmp(argv[i],"/s")==0)search_sub_directory=1;
   else if(stricmp(argv[i],"-h")==0
      || stricmp(argv[i],"/h")==0)with_hide=1;
   else{
    struct file_name_strct *fnc;

    fnc=NEW(struct file_name_strct,1);
    fnc->fname=new_strcat(NULL,argv[i]);
    fnc->next =NULL;

    if(file_name_end==NULL)file_name_top   =fnc;
    else          file_name_end->next=fnc;

    file_name_end=fnc;
   }
  }

  return 1;
 }

 display_total_lines()
 {
  if(file_name_top==NULL){
   printf("\n===== 標準入力 =====\n\n");

   if(display_file_total_lines()== -1)return -1;

   printf("\n==================\n");
   printf("\n合計 %10u 行\n",grand_total_lines);
  }

  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;
 }

 display_file_total_lines()
 {
  unsigned int lines;

  char data[READ_BUF_SIZE];
  int ldata,i;

  lines=0;

  while(ldata=fread(data,1,READ_BUF_SIZE,fp)){
   for(i=0;i<ldata;i++){
    if(data[i]=='\n')lines++;
   }
  }

  if(fname)printf(" %7d 行 %s\n",lines,fname);
  else   printf(" %7d 行\n"  ,lines   );

  grand_total_lines+=lines;

  return 1;
 }

次回からはファイルの行数表示を行なえるようにしていきましょう。
それではまた。

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

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