プログラムは8行で、3周まわれば終わります。難しいのは1か所だけ ── 2進数の右から6桁を切り取るには、何で割ればよいかです。そこが分かれば、空欄は決まります。
難易度 ★★★★(当サイトの目安)── 符号化の規則を読み解いたうえで、6ビットずつ区切る(64で割る)と気づく必要がある。4段階の決め方は科目B 最短合格の戦略にあります。
出題
読み慣れない言葉があっても大丈夫です。次の節にまとめてあります。
出典:基本情報技術者試験 科目B サンプル問題 問16
次のプログラム中の に入れる正しい答えを,解答群の中から選べ。二つの には,同じ答えが入る。ここで,配列の要素番号は1 から始まる。
Unicode の符号位置を,UTF-8 の符号に変換するプログラムである。本問で数値の後ろに“(16)”と記載した場合は,その数値が16 進数であることを表す。
Unicode の各文字には,符号位置と呼ばれる整数値が与えられている。UTF-8 は,Unicode の文字を符号化する方式の一つであり,符号位置が800(16) 以上FFFF(16) 以下の文字は,次のように3 バイトの値に符号化する。
3 バイトの長さのビットパターンを 1110xxxx 10xxxxxx 10xxxxxx とする。ビットパターンの下線の付いた“x”の箇所に,符号位置を2 進数で表した値を右詰めで格納し,余った“x”の箇所に,0 を格納する。この3 バイトの値がUTF-8 の符号である。
例えば,ひらがなの“あ”の符号位置である3042(16) を2 進数で表すと 11000001000010 である。これを,上に示したビットパターンの“x”の箇所に右詰めで格納すると,1110xx11 10000001 10000010 となる。余った二つの“x”の箇所に0 を格納すると,“あ”のUTF-8 の符号 11100011 10000001 10000010 が得られる。
関数encode は,引数で渡されたUnicode の符号位置をUTF-8 の符号に変換し,先頭から順に1 バイトずつ要素に格納した整数型の配列を返す。encode には,引数として,800(16) 以上FFFF(16) 以下の整数値だけが渡されるものとする。
〔プログラム〕
○整数型の配列: encode(整数型: codePoint)
/* utf8Bytesの初期値は,ビットパターンの“x”を全て0に置き換え,
8桁ごとに区切って,それぞれを2進数とみなしたときの値 */
整数型の配列: utf8Bytes ← {224, 128, 128}
整数型: cp ← codePoint
整数型: i
for (i を utf8Bytesの要素数 から 1 まで 1 ずつ減らす)
utf8Bytes[i] ← utf8Bytes[i] + (cp ÷ の余り)
cp ← cp ÷ の商
endfor
return utf8Bytes
解答群
ア ((4 - i) × 2) / イ (2 の (4 - i)乗) / ウ (2 の i 乗)
エ (i × 2) / オ 2 / カ 6
キ 16 / ク 64 / ケ 256
※ 解答群は原本では3列に並んでいます(字句は変えていません)。
答えだけ先に見る
正解は ク(64)です。
この問題に出てくる言葉
言葉と記号 ── 読み方の早見表をひらく
| 書き方 | この問題では |
|---|---|
| 符号位置 3042(16) | 文字ごとに決まっている整数。「あ」は 3042(16)。(16) は16進数の印です |
| ビットパターン 右詰め | 0 と 1 を並べた型。右詰めは、右端をそろえて入れること。左に余った x には 0 を入れます |
… の余り … の商 |
整数どうしの割り算の余りと商。7 ÷ 3 は 2 余り 1 なので、余りは 1、商は 2 |
{224, 128, 128} |
3つの整数が入った配列。utf8Bytes[1] が 224、utf8Bytes[3] が 128 です |
for (i を utf8Bytesの要素数 から 1 まで 1 ずつ減らす) |
i を 3, 2, 1 の順に変えながら endfor までをくり返す。後ろの要素から処理します |
/* … */ |
注釈。読む人への説明で、実行には関係ありません |
x に入るのは、右から6桁ずつ
まず、問題文の「あ」の例を見直します。符号位置 3042(16) の2進数 11000001000010 を、右から6桁ずつ区切ると、x の並びとぴったり重なります。
「あ」の2進数を、右から6桁ずつ x に入れる
琥珀の数字= 符号位置の2進数。上下で同じ桁にそろえてあります
出題の例(3042(16) = 11000001000010)
ビットパターン
1110xxxx 10xxxxxx 10xxxxxx
右から6桁ずつ
区切った2進数
11 000001 000010
x に入れると
11100011 10000001 10000010
いちばん左に残った 11 は2桁しかないので、1バイト目の x の残り2つには 0 が入ります。
2バイト目と3バイト目の x は6個ずつ。だから符号位置の2進数は、右から6桁ずつ区切って、後ろのバイトから入れていけばよい。プログラムの for も i を 3, 2, 1 と後ろから動かしています。
空欄のある for の中身は2行です。
〔プログラム〕空欄のある for
for (i を utf8Bytesの要素数 から 1 まで 1 ずつ減らす)
utf8Bytes[i] ← utf8Bytes[i] + (cp ÷ の余り)
cp ← cp ÷ の商
endfor
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
入れ方は足し算です。utf8Bytes の初期値 {224, 128, 128} は、注釈のとおり x を全部 0 にした 11100000 10000000 10000000。x の場所が 0 なので、6桁の値を足せば、そのまま x の場所に入ります。足すのは6桁に収まる値なので、x の外へ繰り上がることはありません(128 + 2 = 130 は 10000000 + 000010 = 10000010)。
つまり1行目で cp の右から6桁を utf8Bytes[i] に足し、2行目で その6桁を cp から取り除く。次の周では、取り除いたあとの右から6桁が、1つ前のバイトに入ります。
右から6桁を切り取る計算を決める
10進数で考えると分かりやすくなります。5678 の右から2桁を取り出すには 100 で割ります。5678 ÷ 100 は 56 余り 78 ── 余りが右2桁、商が残りの左側です。100 は 10 を2回かけた数。10進数は1桁左へ行くごとに 10 倍になるからです。
2進数は1桁左へ行くごとに 2 倍です。だから2進数の右から6桁を取り出すには、2 を6回かけた 64 で割る。余りが右6桁、商が残りの左側です。空欄は2つとも 64 です。
「あ」の符号位置 3042(16) は、10進数に直すと 3 × 4096 + 4 × 16 + 2 = 12354 です。これを encode に通して確かめます。
「あ」(12354)を encode に通す
琥珀の数字= この周で切り取る右6桁(3周目は残りが2桁だけ)と、それが入った場所。2進数は右端をそろえてあります
1周目 i = 3
周の始めの cp
12354 = 11000001000010
cp ÷ 64 の余り
2 = 000010
utf8Bytes[3]
128 + 2 = 130 = 10000010
cp ÷ 64 の商
(次の cp)
193 = 11000001
2周目 i = 2
周の始めの cp
193 = 11000001
cp ÷ 64 の余り
1 = 000001
utf8Bytes[2]
128 + 1 = 129 = 10000001
cp ÷ 64 の商
(次の cp)
3 = 11
3周目 i = 1
周の始めの cp
3 = 11
cp ÷ 64 の余り
3 = 11
utf8Bytes[1]
224 + 3 = 227 = 11100011
cp ÷ 64 の商
(次の cp)
0 = (残りなし)
3周で utf8Bytes = {227, 129, 130} = 11100011 10000001 10000010。問題文にある「あ」の UTF-8 と同じです。
解答群を見る
空欄は 64 ── ク です。
答え合わせ
ク 64
ほかの数で割ると、切り取る桁数がずれます。2 は1桁、16 は4桁、256 は8桁を切り取る数で、どれも x の6個と合いません。6 は桁数そのもので、「6桁」と「6 で割る」の取り違えです。i を使う式(ア〜エ)は周ごとに割る数が変わります。たとえばアは1周目(i = 3)に 2 で割るので、1桁しか切り取れません。2バイト目と3バイト目はどちらも6桁なので、割る数は周ごとに変わってはいけません。
1バイト目の x は4個しかないのに 64 でよいのは、符号位置が FFFF(16) 以下=2進数で16桁以下だからです。6桁を2回取ると、残りは4桁以下しかありません。3周目の余りはその残り全部で、4個の x に収まります。
次に読む
この問題でどこに手間取ったかで、行き先が変わります。
この記事で引用した資料
いずれも独立行政法人情報処理推進機構(IPA)が公表したものです。IPAは公表済みの試験問題について、教育目的での使用に許諾および使用料を不要としていますが、著作権は放棄していません。本記事では問題文を改変せずに引用しています。

