基本情報技術者試験の過去問と解説
[TOP] [午前分野別] [午後分野別] [キーワード索引] [令和元年秋午前] [令和元年秋午後]

平成27年 秋期 基本情報技術者 午後 問09
問09   C言語

 次のCプログラムの説明及びプログラムを読んで,設問1,2に答えよ。

〔プログラムの説明〕

 入退室が制限されているエリア(以下,制限エリアという)の入退室の状況を 記録したファイルを読み込んで,入退室状況を印字するプログラムである。

 X社では,IC カードを用いた入退室システムを導入して,制限エリアの入退室を 管理している。図1に,X社の制限エリアのレイアウトを示す。 ドアは5か所(ドア番号 11,12,21,22,31)あり,各ドアの内外にカードリーダが設置されている。 入室時と退室時には,カードリーダに IC カードをかざしてドアを開錠する。 各ドアのカートリーダに IC カードをかざす都度,その入退室の状況を記録した1行の入退室レコードが 入退室ログに書き込まれる。

 制限エリアには,機密度に応じたレベルが設定されている。レベルは,外部からその部屋へ入るまでに 開錠して通過する必要があるドアの数で表す。ドア番号は,10 の位がレベルを表す。 入退室できるレベルは,IC カードごとに設定されている。


図1 X社の制限エリアのレイアウト

(1) 入退室レコードは 33 桁の固定長の文字列で,次に示す項目から成る。

@ カード ID は,IC カードを識別する ID であり,英数字から成る。

A 日付と時刻は,カードリーダに IC カードをかざした日付と時刻である。

B 入退は,“I”(入室)又は“O”(退室)である。

C 可否は,“A”(開錠)又は“R”(拒否)である。

D 名前は,IC カードの使用者を識別する名前であり,英数字と空白から成る。

E レコードの終端には,改行文字が付いている。

(2) 入退室ログから印字したい日付・時間帯の入退室レコードを抽出し,カード ID, 日付及び時刻(先頭の 18 桁)で昇順に整列したファイルを Access.Log とする。 図2に,Access.Log のレコードの例を示す。


図2 Access Log のレコードの例

(3) Access.Log から読み込んだ1レコードごとに,カード ID,名前,日時,ドア番号, 入退,可否から成る1行を,図3に示す様式で印字する。日時以降の項目は, IC カードをかざしたドアのレベルに応じて 19,39,59 桁目のいずれかから印字する。


図3 図2のレコードの例を用いた印字結果

@ カード ID 及び名前は,同一カード ID が続くとき,先頭の行だけに印字する。

A 日時は,“月・日時:分”の形式で印字する。

B 入退は,“I”なら“IN”と,“O”なら“OUT”と印字する。

C 可否は,“A”なら何も印字せず,“R”なら“IN”又は“OUT”の直前に“(R)”を印字する。

(4) ライブラリ関数 strcmp(s1,s2)は,文字列 s1 と s2 を比較し,s1 < s2 のとき 負の値を,s1 = s2 のとき 0 を,s1 > s2 のとき正の値を,それぞれ返す。 また,ライブラリ関数 strcpy(s1,s2)は,文字列 s2 を s1 に複写する。

〔プログラム〕

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

      FILE *logFile;
      int logEOF = Ø
      char cardID[5] = "----", date[9] = "--------", time[7] = "------",
           door[3] = "--",  dir[2] = "-", act[2] = "-",
           name[11] = "----------";
@ → char lastID[5] ="----";

      void getRecord();
A → void putRecord();

      void main() {
         logFile = fopen("Access.Log", "r");
         getRecord();
         while (logEOF != EOF) {
B →       putRecord();
C →       getRecord();
D →    }
         fclose(logFile);
     }

     void getRecord() {
        if (fscanf(logFile, "%4c%8c%6c%2c%1c%1c%1Øc\n",
                   cardID, date, time, door, dir, act, name) == EOF)
           
        /*ファイルの終わりに達したとき fscanf は EOF (負の整数定数)を返す */
     }

     void putRecord() {
        int putSpace;

        if (strcmp(cardID, lastID) == Ø)
           printf("%18s", " ");
        else {
           printf("\n%4s  %1Øs  ", cardID, name);
E →      ;
F →   }
        putSpace = ;
        while (Ø < putSpace--) {
           printf("%2Øs", " ");
G →   }
        printf("%.2s-%.2s %.2s:%.2s %2s ",
               date+4, date+6, time, time+2, door);
        if(strcmp(act, "R") == Ø)  printf("%s",   "(R)");
        if(strcmp(dir, "I")== Ø)  printf("%s\n", "IN" );
        else                       printf("%s\n", "OUT");
     }
設問1 次プログラム中の に入れる正しい答えを, 解答群の中から選べ。

a,b に関する解答群

ア cardID = lastID        イ LastID = cardID       
ウ logEOF = Ø        エ logEOF = EOF       
オ strcpy(cardID, "     ")        カ strcpy(cardID, lastID)       
キ strcpy(cardID, "     ")        ク strcpy(lastID, cardID)       

c に関する解答群

ア door[Ø] - 'Ø' - 1        イ door[Ø] - 'Ø'       
ウ door[Ø] - 'Ø' + 1        エ door[Ø] - '3'       
解答 a ←クリックすると正解が表示されます

解答 b ←クリックすると正解が表示されます

解答 c ←クリックすると正解が表示されます

設問2 次の記述中の に入れる正しい答えを, 解答群の中から選べ。

 

 プログラムを変更して,入退室順序の整合性を検査する処理を追加する。

 各入退室者は,最初は外部(レベル 0 )にいて,最後は外部に出るものとする。 正しく入退室していれば,制限エリアのレベルは 0 から始まり,ドアを通過する都度, レベルが1ずつ変化し,最後は 0 に戻るはずである。 しかし,抽出した日付・時間帯よりも前(後)に入室(退室)している場合や, IC カードをかざさずに人の後についてドアを通過した場合などには,順序の不整合が起きる。

 なお,カードリーダに IC カードをかざしてドアが開錠した場合は,必ず入退室するものとする。

(1) 順序の不整合が起きた場合は,本来入退室の記録があるべき行のレベル1の印字位置に “***** Level x-->y”と印字する。これは,入退室者がこの前後にレベル x から y ヘ 移動したはずであるが,その記録がないことを意味する。

(2) 図4に,不整合がある入力レコードの例を示す。図4のレコードを用いて変更後の プログラムを実行すると,印字結果は,図5のようになる。


図4 不整合がある入力レコードの例


図5 不整合がある入力レコードの例(図4)のときの印字結果

 プログラムを,次のように変更する。

(1) プログラムに,次の文を追加する。

(2) プログラムの末尾に,次の二つの関数を追加する。

void checkLevel() {
   int afterLevel, beforeLevel, doorLevel;

   doorLevel = door[O] - 'Ø';
   beforeLevel = doorLevel;
   afterLevel  = doorLevel;
   if (strcmp(dir, "I")  == Ø)
      beforeLevel = doorLevel - 1;
   else
      afterLevel = doorLevel - 1;
   if (strcmp(act, "R") == Ø)
      afterLevel = beforeLevel;
   if (level != beforeLevel)
      printf("***** Level %d-->%d\n%18s",
              ,  , " ");
   level = afterLevel;
}

void clearLevel() {
   if (logEOF == EOF ll (strcmp(cardID, lastID) != Ø))
      if (Level > Ø) {
         printf("%18s***** Level %d-->Ø\n", " ", level);
         level = Ø
      }
}
d,e に関する解答群 ア 行B        イ 行C        ウ 行D

エ 行F        オ 行F        カ 行G

f,g に関する解答群

ア afterLevel          イ beforeLevel

ウ doorLevel          エ level

解答 d ←クリックすると正解が表示されます

解答 e ←クリックすると正解が表示されます

解答 f ←クリックすると正解が表示されます

解答 g ←クリックすると正解が表示されます


[←前の問題] [次の問題→] [問題一覧表] [分野別] [基本情報技術者試験TOP ]