基本情報技術者 2014年 秋期 午後 問09
次のCプログラムの説明及びプログラムを読んで、設問1,2に答えよ。
B社では、セキュリティ管理のために、システムファイルから定期的に、システムの運用・保守用の利用者ID一覧を作成し、ファイルで保管している。
これらの利用者IDに付加できる特権には、システムファイルの更新などができるシステム特権(以下、特権Sという)及びバックアップ作業などのために全てのファイルを参照できるオペレーション特権(以下、特権Oという)の2種類がある。
B社では、セキュリティ管理強化の一環で、利用者IDの追加・削除や特権の付加・解除が、申請に基づいて正しくシステムに反映されているかどうかを検証することになった。そのために、最新及び1世代前の利用者ID一覧を比較して、この間の利用者ID及び特権の登録内容の差異を印字するプログラムを作成する。
〔プログラムの説明〕
(1) 最新の利用者ID一覧をファイルNewFileから、1世代前の利用者ID一覧をファイルOldFileから、それぞれ読み込む。レコードは利用者IDの昇順に整列されている。
(2) 1レコードは、利用者ID(1~8桁)、利用者名(1~10桁)、属性(1桁)及び最終使用日(8桁)の4項目から成る。各項目は、空白文字で区切られている。
① 利用者ID及び利用者名は、英数字から成る文字列である。
② 属性は、次に示す内容の8ビット長のビット列0100sogrである。
上位4ビット: 固定値0100
ビットs: 特権Sが付加されていれば1、付加されていなければ0
ビットo: 特権Oが付加されていれば1、付加されていなければ0
ビットg及びr: その他の属性
③ 最終使用日は、数字から成る文字列で、その利用者IDで最後にログインした年月日を表す。登録後、一度もログインしていない場合は、全桁が“0”である。
(3) 利用者ID及び特権の登録内容の差異は、次のように印字する。
① NewFile中にあってOldFile中にない利用者IDの場合
利用者ID、利用者名の後に“利用者ID追加”と印字する。この利用者IDに特権Sが付加されていれば“特権S付加”、特権Oが付加されていれば“特権O付加”を追加印字する。
② OldFile中にあってNewFile中にない利用者IDの場合
利用者ID、利用者名の後に“利用者ID削除”と印字する。この利用者IDに特権Sが付加されていれば“特権S解除”、特権Oが付加されていれば“特権O解除”を追加印字する。
③ NewFile及びOldFileの両方にあり、特権Sと特権Oの少なくとも一方の付加状況が変わった利用者IDの場合
利用者ID、利用者名の後に“特権S付加”、“特権S解除”、“特権O付加”、“特権O解除”の該当する全てを印字する。
(4) 入力ファイルNewFile及びOldFileのデータ例を図1に、図1のデータ例を用いた実行結果を図2に、それぞれ示す。

(5) ライブラリ関数strcmp(s1, s2)は、文字列s1とs2を比較し、s1 < s2のとき負の値を、s1 = s2のとき0を、s1 > s2のとき正の値を、それぞれ返す。

設問1:
問題文を見る プログラム中のに入れる正しい答えを、解答群の中から選べ。
aに関する解答群
ア:(NewEof != EOF) && (OldEof != EOF)
イ:(NewEof != EOF) || (OldEof != EOF)
ウ:NewEof != OldEof
エ:NewEof == OldEof
bに関する解答群
ア:((NewAttr & OldAttr) & (BitS + BitO)) != 0x00
イ:((NewAttr | OldAttr) & (BitS + BitO)) != 0x00
ウ:(NewAttr & (BitS + BitO)) != (OldAttr & (BitS + BitO))
エ:(NewAttr | (BitS + BitO)) != (OldAttr | (BitS + BitO))
cに関する解答群
ア:ReadNewRecord();
イ:ReadNewRecord(); ReadOldRecord();
ウ:ReadOldRecord();
dに関する解答群
ア:(NewAttr & (BitS + BitO)) != 0x00
イ:NewAttr > OldAttr
ウ:strcmp(NewID, OldID) < 0
エ:strcmp(NewID, OldID) > 0
模範解答
a:イ、b:ウ、c:イ、d:ウ
解説
解答の導き方
このプログラムは、利用者ID(Identification:利用者を識別する文字列)の昇順に並んだ2つのファイルを先頭から順に比べ、登録内容の差を印字します。昇順とは、利用者IDを小さい順に並べることです。
a:どちらかのファイルに未処理のレコードがある間、繰り返します。
ファイルの終わりを表すEOF(End Of File:ファイルの終わりを示す値)は、レコードを読み込む関数によってNewEofまたはOldEofに設定されます。まだ終わりに達していないファイルでは、これらの値はEOFではありません。
片方のファイルが先に終わっても、もう片方に未処理のレコードが残っていることがあります。そのレコードも追加または削除として印字する必要があるので、繰返し条件は「NewFileまたはOldFileのどちらか一方が終わっていない」です。したがって、aには「(NewEof != EOF) || (OldEof != EOF)」が入ります。
両方のファイルが終わったときだけ、繰返しを終了します。条件を「かつ」にすると、片方が先に終わった時点で処理が止まり、もう片方に残るレコードを印字できません。
b:特権Sまたは特権Oの付加状況に違いがあるかを調べます。
BitSは特権Sに対応するビット、BitOは特権Oに対応するビットです。ビット演算の「&」を使うと、属性のうち指定したビットだけを取り出せます。BitSとBitOを合わせたBitS + BitOを使えば、特権Sと特権Oの情報だけを取り出せます。
ここでは属性全体ではなく、この2種類の特権だけを比べます。ビットgとrは「その他の属性」なので、比較に含めません。したがって、取り出した特権情報どうしが異なるかを調べる「(NewAttr & (BitS + BitO)) != (OldAttr & (BitS + BitO))」が入ります。
例えば図1のAE002では、NewFileの属性48とOldFileの属性44は16進数です。特権Sと特権Oのビットだけを取り出すと、それぞれ0x08と0x04になります。値が異なるため、特権の変更を印字します。どちらの特権が付加・解除されたかは、その後の個別のif文で判定します。
c:同じ利用者IDを両方のファイルで処理したら、両方から次のレコードを読みます。
NewIDとOldIDが同じなら、その利用者IDは新旧の両方にあります。特権の変更を印字するかどうかにかかわらず、両方のレコードは処理済みです。次の利用者IDどうしを比べるため、NewFileとOldFileの両方から読み込む必要があります。したがって、cには「ReadNewRecord(); ReadOldRecord();」が入ります。
d:NewFile側の利用者IDがOldFile側より小さいかを調べます。
利用者IDは両方のファイルで昇順に並んでいます。strcmp(string compare:文字列を比較する関数)は、NewIDがOldIDより小さいときに負の値を返します。
NewIDがOldIDより小さい場合、その利用者IDはOldFileの現在位置にはなく、昇順に並んだOldFileの後ろにも現れません。つまりNewFileにだけある利用者IDなので、「利用者ID追加」と印字し、NewFileだけ次のレコードに進みます。この条件が「strcmp(NewID, OldID) < 0」です。
反対にOldIDのほうが小さい場合は、OldFileにだけある利用者IDです。削除として印字し、OldFileだけ次のレコードに進みます。
誤りやすいポイント
-
aを「かつ」にする
片方のファイルが終わっただけで繰返しが終了します。もう片方に残った追加・削除の対象を処理できません。 -
属性全体を比較する
特権S・特権O以外のビットg・rまで比較すると、今回印字する必要のない属性の違いも検出してしまいます。特権のビットだけを取り出して比べます。 -
bで、共通して付加されているかを調べる
共通の特権があるかどうかではなく、新旧で付加状況が変わったかどうかを調べます。取り出した特権情報どうしを比較します。 -
同じ利用者IDのとき、片方だけ読み進める
同じ利用者IDの新旧レコードは両方とも処理済みです。片方だけ進めると、次回の比較で対応関係がずれてしまいます。 -
利用者IDの大小と追加・削除を逆にする
NewIDが小さければNewFileにだけあるため追加です。OldIDが小さければOldFileにだけあるため削除です。
FAQ
Q:同じ利用者IDが両方にあって、特権に変更がない場合も、両方のファイルを読み進めますか?
A:読み進めます。変更がないため印字しない場合でも、そのレコード自体は比較済みだからです。
A:読み進めます。変更がないため印字しない場合でも、そのレコード自体は比較済みだからです。
Q:特権Sと特権Oを別々に判定するのはなぜですか?
A:bでは「少なくとも一方に変更があるか」を調べます。その後の個別のif文で、特権Sが付加・解除されたのか、特権Oが付加・解除されたのかを判定して印字するためです。
A:bでは「少なくとも一方に変更があるか」を調べます。その後の個別のif文で、特権Sが付加・解除されたのか、特権Oが付加・解除されたのかを判定して印字するためです。
Q:片方のファイルが終わった後も、比較を続けられるのはなぜですか?
A:ファイルの終わりに達した側の利用者IDには、通常の利用者IDより大きい値として「\xFF」が設定されます。そのため、もう片方に残る利用者IDを順に比べ、追加または削除として処理できます。両方が終わると、aの条件が偽になり繰返しを終了します。
A:ファイルの終わりに達した側の利用者IDには、通常の利用者IDより大きい値として「\xFF」が設定されます。そのため、もう片方に残る利用者IDを順に比べ、追加または削除として処理できます。両方が終わると、aの条件が偽になり繰返しを終了します。
関連キーワード: EOF、strcmp、ビット演算、ビットマスク、昇順比較
設問2:
問題文を見る 次の記述中のに入れる正しい答えを、解答群の中から選べ。
利用者IDが有効に使用されているかどうかを検証するために、一定期間未使用、又は現在使用不可の利用者ID一覧を印字するプログラムを作成する。
(1) 最新の利用者ID一覧をファイルNewFileから、一定期間前の世代の利用者ID一覧をファイルOldFileから、それぞれ読み込む。
(2) 次の①、②の少なくとも一方に該当する利用者IDについて、利用者ID、利用者名の後に、①に該当する場合は“現在使用不可”を、②に該当する場合は“期間中未使用”を印字する。
① NewFile中にあって、属性のビットrが1である利用者ID
ここで、属性のビットrは、利用者IDが使用可能なら0、使用不可なら1である。
② NewFile及びOldFileの両方にあって、最終使用日の値が等しい利用者ID(全桁が“0”同士で等しい場合を含む)
(3) 図3に、図1のデータ例を用いた実行結果を示す。

この処理を実装するためには、プログラム中のwhile文のブロック内(αで示した部分)を次のように変更すればよい。ここで、プログラム中のaには、正しい答えが入っているものとする。

e, fに関する解答群
ア:(NewAttr & BitR) != (OldAttr & BitR)
イ:(NewAttr & BitR) == BitR
ウ:strcmp(NewDate, OldDate) == 0
エ:strcmp(NewID, OldID) == 0
オ:strcmp(NewID, OldID) == 0 && (NewAttr & BitR) == BitR
カ:strcmp(NewID, OldID) == 0 && strcmp(NewDate, OldDate) == 0
模範解答
e:イ、f:カ
解説
解答の導き方
この設問では、利用者IDごとに「現在使用不可」と「期間中未使用」のどちらに当てはまるかを判定する条件を選びます。2つの条件は別々に判定するので、順に確認します。
まず、プログラムは利用者ID順に並んだNewFileとOldFileを比べています。こうして、どちらのファイルにあるIDか、両方にあるIDかを順番に確認する方法を、ここではマージ処理(並んだ複数の一覧を先頭から比べていく処理)と考えると分かりやすいです。
while文の中では、strcmp(NewID, OldID) <= 0の場合に、NewFileのレコードを調べます。strcmpは、2つの文字列の大小や一致を比べる関数です。
- NewIDがOldIDより小さいときは、NewFileにだけあるIDです。
- NewIDとOldIDが等しいときは、両方のファイルにある同じIDです。
- NewIDがOldIDより大きいときは、OldFileにだけあるIDなので、OldFileのレコードを先に読み進めます。
この分岐を踏まえて、各条件を考えます。
現在使用不可を判定する条件e
問題文では、現在使用不可なのは「NewFile中にあって、属性のビットrが1である利用者ID」とされています。つまり、判定に必要なのは、NewFile側の属性のビットrです。OldFile側の属性と比べる必要はありません。
プログラムでは、ビットを取り出すためにビットマスク(特定のビットだけを調べる値)を使います。NewAttr & BitRは、NewAttrのうちビットrだけを取り出す計算です。ビット単位のAND演算では、両方の値で1になっているビットだけが結果に残ります。
BitRは0x01なので、ビットrが1ならNewAttr & BitRの結果はBitRと等しくなります。ビットrが0なら結果は0です。したがって、ビットrが1かどうかを判定する条件は、選択肢のイです。
この判定に、NewIDとOldIDが等しいという条件を加えてはいけません。NewFileにだけあるIDでも、ビットrが1なら「現在使用不可」として印字する必要があるからです。
期間中未使用を判定する条件f
問題文では、期間中未使用なのは「NewFile及びOldFileの両方にあって、最終使用日の値が等しい利用者ID」とされています。そのため、次の2つを両方確かめます。
- 利用者IDが同じであること
- 最終使用日が同じであること
前者はstrcmp(NewID, OldID) == 0、後者はstrcmp(NewDate, OldDate) == 0で判定できます。よって、fにはカが入ります。
日付だけを比べても、別の利用者の最終使用日がたまたま同じ場合があります。利用者IDも同じか確かめる必要があるのは、そのためです。また、最終使用日が両方とも全桁「0」の場合も、問題文で「等しい場合を含む」とされています。文字列として同じであれば、この条件で判定できます。
図1のAE001を例にすると、NewFileの属性41ではビットrが1で、最終使用日はOldFileと同じです。そのため、eとfの両方が成立し、「現在使用不可」と「期間中未使用」の両方が印字されます。一方、AE003は最終使用日が同じですが、NewFileのビットrは0なので、fだけが成立します。
誤りやすいポイント
- eでNewIDとOldIDの一致まで求めてしまう
- 現在使用不可かどうかは、NewFileの属性だけで判定します。NewFileにだけあるIDも対象になるため、IDの一致条件は不要です。
- fで最終使用日だけを比べてしまう
- 日付が同じでも利用者IDが違えば、同じ利用者の期間中の使用状況とは判断できません。IDと日付の両方を比較します。
- 属性のビットrと、特権のビットsやoを混同する
- この設問で現在使用不可かを調べるのはビットrです。NewAttr & BitRがBitRと等しいかどうかを確認します。
- 「期間中未使用」を、最終使用日が0のときだけと考える
- 問題文では、2つの最終使用日の値が等しいことが条件です。全桁「0」同士だけでなく、ほかの同じ日付も対象です。
FAQ
Q: NewFileにだけある利用者IDは、期間中未使用として印字されますか?
A: いいえ。期間中未使用の条件fには、NewIDとOldIDが等しいことが含まれています。NewFileにだけあるIDは、fを満たしません。ただし、ビットrが1なら「現在使用不可」として印字されます。
A: いいえ。期間中未使用の条件fには、NewIDとOldIDが等しいことが含まれています。NewFileにだけあるIDは、fを満たしません。ただし、ビットrが1なら「現在使用不可」として印字されます。
Q: 最終使用日が両方とも「00000000」の場合も、期間中未使用になりますか?
A: はい。問題文では全桁「0」同士で等しい場合も含むとされています。利用者IDも同じなら、期間中未使用として扱います。
A: はい。問題文では全桁「0」同士で等しい場合も含むとされています。利用者IDも同じなら、期間中未使用として扱います。
Q: 「現在使用不可」と「期間中未使用」は同時に印字されることがありますか?
A: あります。2つの条件は別々に判定されます。図1のAE001のように、ビットrが1で、かつ最終使用日も前の世代と同じなら、両方が印字されます。
A: あります。2つの条件は別々に判定されます。図1のAE001のように、ビットrが1で、かつ最終使用日も前の世代と同じなら、両方が印字されます。
関連キーワード: ビット演算、ビットマスク、文字列比較、マージ処理、利用者ID管理




