第4回文字列操作2(5月11日)の解答例

練習1

演習中に配ったものよりも、while 文の終了条件がうまくできています。
/* prac0401.c */
#include <stdio.h>

int split_number_name( char *line, int *dn, char *name, int len )
{
  int n, i, pos;

  n = 0;
  for( i = 0; i < 2; i += 1 ){
    if( (line[i] < '0') || ('9' < line[i]) )
      return 0;
    n = n * 10 + line[i] - '0';
  }
  *dn = n;

  if( line[i] != '_' )
    return 0;
  i += 1;

  pos = 0;
  while( line[i] != '.' ){
    if( len <= pos )
      return 0;
    switch( line[i] ){
    case '\0':
      return 0;
    case '_':
      name[pos] = ' ';
      break;
    default:
      name[pos] = line[i];
    }
    pos += 1;
    i += 1;
  }
  name[pos] = '\0';
  return 1;
}



int main()
{
  int dn;
  char line[100], name[100];

  while( fgets(line,100,stdin) != NULL ){
    if( split_number_name(line, &dn, name, 100) )
      printf("%d\t%s\n", dn, name);
  }
  return 0;
}

練習2

nc_get の終了の仕方は、仕様が悪い。nc_get には返り値を持たせ、フォー マット通りの場合は 1 を返し、フォーマットと異なる場合は 0 を返し、入力 無しの場合は 2 を返すなどの工夫が必要である。
/* namecard.c */
// 第4回目のために拡張

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "namecard.h"

// ==================================================================
// アクセサ(構造体の変数を読み書きするための関数)

// ------------------------------------------------------------------
// 変数 name の操作

// 指定位置の文字の読み書き
void nc_put_name_at(NameCard *nc, char c, int pos)
{
  nc->name[pos] = c;
}

char nc_get_name_at(NameCard *nc, int pos)
{
  return nc->name[pos];
}

// 名前の読み書き
char *nc_get_name(NameCard *nc)
{
  return nc->name;
}

void nc_put_name(NameCard *nc, char *str)
{
  strncpy(nc->name, str, NAME_LEN);
}

// 名前の消去
void nc_clear_name(NameCard *nc)
{
  nc_put_name_at(nc,'\0',0);
}



// ------------------------------------------------------------------
// 変数 age の操作
void nc_put_age(NameCard *nc, int n)
{
  nc->age = n;
}

int nc_get_age(NameCard *nc)
{
  return nc->age;
}

// ------------------------------------------------------------------
// ドライバーナンバーの操作
void nc_put_driver_number(NameCard *nc, int dn)
{
  nc->number = dn;
}

int nc_get_driver_number(NameCard *nc)
{
  return nc->number;
}


// ==================================================================
// 構造体の確保と解放

NameCard *nc_open()
{
  NameCard *ans;

  ans = (NameCard *)malloc(sizeof(NameCard));
  if( ans == NULL ){
    fprintf(stderr,"nc_open: malloc failed\n");
    exit(1);
  }
  nc_put_age(ans, 0);
  nc_put_driver_number(ans, 0);
  nc_clear_name(ans);
  return ans;
}


void nc_close(NameCard *nc)
{
  free(nc);
}


// ==================================================================
// フォーマット通りの読み書き

// 末尾の改行コードをカットする
// ※ ここのファイル namecard.c の中だけで使う関数なので
//    static としている。
static void cut_return_code( char * line )
{
  int l;

  l = strlen( line );
  if( line[l-1] == '\n')
    line[l-1] = '\0';
}



// 1行のデータを分割
// ※ ここのファイル namecard.c の中だけで使う関数なので
//    static としている。
static int split_number_name_age(char *line, int *dn, char *name, int len, int *age)
{
  int n, i, pos;

  i = 0; // i は、入力文字列 line の参照位置

  // 数字を表す文字列を参照し、整数 int に変換
  n = 0;
  while( line[i] != '\t' ) { // タブが正しい境界文字
    if( (line[i] < '0') || ('9' < line[i]) )
      return 0;
    n = n * 10 + line[i] - '0';
    i += 1;
  }
  *dn = n;
  i += 1; // タブを読み飛ばす

  // 名前を表す文字列を参照
  // 「(年齢)」が現れるまで、または「\0」までを参照
  pos = 0;
  while( line[i] != '(' ){
    if( len <= pos )
      return 0;
    if( (line[i] == '\0') ){ // 「\0」のとき終了
      name[pos] = '\0';
      *age = 0;
      return 1;
    }
    name[pos] = line[i];
    pos += 1;
    i += 1;
  }
  name[pos] = '\0';
  i += 1;

  // 「年齢)」のうち年齢を抽出
  n = 0;
  while( line[i] != ')' ) {
    if( (line[i] < '0') || ('9' < line[i]) )
      return 0; // 「閉じ括弧で終了しなければスキャン失敗」
    n = n * 10 + line[i] - '0';
    i += 1;
  }
  *age = n;

  return 1;
}



// 1行データをファイルから入力する
void nc_get(NameCard *dst, FILE *fin)
{
  int dn, age;
  char line[NAME_LEN + 32];
  char name[NAME_LEN];

  if( fgets(line, NAME_LEN + 32, fin) == NULL )
    exit(0); // 正常終了、仕様がよくない。。。
  cut_return_code( line ); // 改行コードをカットする。
  if( split_number_name_age(line, &dn, name, NAME_LEN, &age) ){
    nc_put_driver_number(dst, dn);
    nc_put_name(dst, name);
    nc_put_age(dst, age);
  } else
    exit(1); // 異常終了、仕様が良くない。。。
}


// 1行データをファイルに出力する
void nc_put(NameCard *src, FILE *fout)
{
  fprintf(fout,"%d\t%s(%d)\n", 
	  nc_get_driver_number(src),
	  nc_get_name(src),
	  nc_get_age(src));
}


(c) Masato TOKUHISA (2007, May 14)