はじめに
このプロジェクトでは、C プログラミング言語を使用してタイピング練習プログラムを作成する方法を案内します。単一の文字列、複数の文字列、およびキーボードレイアウトに基づくタイピングなど、さまざまなタイピングエクササイズを実行する方法を学びます。これらのプログラムは、あなたのタイピング速度と正確性を向上させるのに役立ちます。
👀 プレビュー
Start typing association exercises.
Please enter the hidden character indicated by '?'.
Press the space key to start.
AS?FG
?m,./\
67890-?\
?XCVB
zx?vb
!"?$%
ZXC?B
hjk?;:]
...(omit)...
🎯 タスク
このプロジェクトでは、以下を学びます。
- C を使用してタイピング練習プログラムを作成する方法。
- 単一の文字列、複数の文字列、およびキーボードレイアウトの関連付けのタイピングエクササイズを実装する方法。
- 画面の入出力操作に
cursesライブラリを使用する方法。 - 練習のバリエーションを持たせるために質問の順序をシャッフルする方法。
🏆 成果
このプロジェクトを完了した後、あなたは以下ができるようになります。
- タイピング練習をシミュレートする C プログラムを作成する。
- ユーザー入力を処理し、正しさを検証する。
- ランダムな質問を生成し、質問の順序をシャッフルする。
- 高度な画面入出力操作に
cursesライブラリを利用する。
プロジェクト環境
このプロジェクトでは、curses ライブラリの使用が必要です。以下のコマンドを使用してインストールしてください。
sudo apt-get update
sudo apt-get install libncurses5-dev
コード内の getputch.h ヘッダーファイルの目的は、クロスプラットフォームの画面入出力関数ライブラリを提供することで、プログラムが異なるオペレーティングシステムやコンパイラ環境で正しく画面入出力操作を実行できるようにすることです。特に、改行文字の出力の処理に関してです。
次に、基本的なタイピングエクササイズを紹介します。これには主に、C 言語のポインタ、文字列配列、およびループ構造が関係します。
文字列配列の応用をデモンストレーションします。単一文字の基本的な入力から、複数の無順序な単語での練習までを行います。
文字を入力する
~/project ディレクトリに移動し、プロジェクトファイル typing1a.c を作成します。
cd ~/project
touch typing1a.c
次に、C コードを書いて、文字列の入力ができるタイピング練習ソフトウェアを作成する必要があります。プログラムは以下の通りです。
#include <time.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "getputch.h"
int main(void)
{
int i;
char *str = "How do you do?"; /* 入力する文字列 */
int len = strlen(str); /* 文字列の文字数 */
init_getputch();
printf("Please type as shown.\n");
printf("%s\n", str); /* 入力する文字列を表示 */
fflush(stdout);
for (i = 0; i < len; i++) {
int ch;
do {
ch = getch(); /* キーボードから入力を読み取る */
if (isprint(ch)) {
putch(ch); /* 押されたキーを表示 */
if (ch!= str[i]) /* 間違ったキーが押された場合 */
putch('\b'); /* カーソルを 1 つ戻す */
}
} while (ch!= str[i]);
}
term_getputch();
return 0;
}
プレイヤーは str ポインタが指す文字列 How do you do? を入力する必要があります。ポインタと配列の互換性により、文字列内の文字 'H', 'o',..., '?' は順に str[0], str[1],..., str[13] として表すことができます。
また、変数 len は文字列 str の長さを表し、初期値は 14 です。strlen() は文字列の長さを取得するために使用されます。
for ループは、変数 i の値を 0, 1, 2,... から len 回インクリメントして、文字列内の文字を先頭から末尾まで順に走査します。各反復で、文字 str[i] は 'H', 'o',..., '?' であり、これらが入力する文字です。このタイピング練習では、間違った文字は受け付けません(プレイヤーが正しい文字を入力するまで、プログラムは次の文字に進みません)。この制御は do ループによって保証され、ループ内では以下のアクションが実行されます。
- 入力された文字(
getch関数の戻り値)を変数chに代入します。 - 文字
chが表示可能な文字である場合、putch関数を使用して表示します(改行やタブなどの非表示文字を除く)。 - 文字
chが入力する文字str[i]と等しくない場合、バックスペース文字'\b'を出力してカーソルを 1 つ戻します。これにより、次に入力された文字が同じ位置に表示されます。
上記の手順を完了した後、do ループの制御式 (ch!= str[i]) が評価されます。間違った文字が入力された場合(ch が str[i] と等しくない場合)、do ループが再度開始されます。このとき、プログラムは次の文字に進まず、do... while ループ内の関連部分を再度実行します。正しい文字を入力した後、for ループによって i の値がインクリメントされ、プログラムは次の文字に進みます。すべての文字を入力した後、プログラムはプレイヤーがかかった時間を表示します。
以下のコマンドを使用してコンパイルして実行します。
cd ~/project
gcc -o typing1a typing1a.c -lcurses
./typing1a

速度を上げるために何度も練習することができます。How do you do? の練習が退屈だと感じた場合は、別の文字列を選んで練習することもできます。
入力した文字の消去
~/project ディレクトリに移動し、プロジェクトファイル typing1b.c を作成します。
cd ~/project
touch typing1b.c
では、プログラムを見てみましょう。コードは以下の通りです。
#include <time.h>
#include <stdio.h>
#include <string.h>
#include "getputch.h"
int main(void)
{
int i;
char *str = "How do you do?"; /* 入力する文字列 */
int len = strlen(str); /* 文字列 str の文字数 */
init_getputch();
printf("Please type as shown.\n");
for (i = 0; i < len; i++) {
/* str[i] 以降の文字を表示し、カーソルを先頭に戻す */
printf("%s \r", &str[i]);
fflush(stdout);
while (getch()!= str[i])
;
}
term_getputch();
return 0;
}
このプログラムは前のものと少し異なります。正しい文字が入力されるたびに、1 文字が消え、それ以降の文字が前に移動します。同様に、プレイヤーが正しいキーを入力しない限り、プログラムは次の文字に進みません。プレイヤーがすべての文字を正しく入力すると、すべての文字が消え、プログラムが終了します。
ここで行われる操作は前のプログラムよりも「高度」ですが、実際にはプログラムは短くなっています。for 文の本体はたった 2 つの短い文で構成されています。
printf("%s \r", &str[i]);文でprintf関数に渡される&str[i]はstr[i]へのポインタです。変数iの値が0であるため、ポインタ&str[i]は文字'H'を指し、画面にはstr[0]から始まる文字列How do you do?が表示されます。その後、プログラムはこの文字列の直後に空白文字と復帰文字\rを出力し、カーソルを行頭の'H'の位置に戻します。- 入力された文字(
getch関数の戻り値)がstr[i]と等しくない場合、つまり入力文字が'H'でない場合、while文はプレイヤーが正しい文字を入力するまで繰り返しループし、その時点でwhile文が終了します。 - その後、変数
iの値はfor文の影響により1になります。上の図のように、printf("%s \r", &str[i]);文はstr[1]から始まる文字列ow do you do?を出力し、次に空白文字と復帰文字を出力して、カーソルを先頭の'o'の位置に戻します。その後、続くwhile文の影響で、プレイヤーが'o'を正しく入力するのを待ちます。
以下のコマンドを使用してコンパイルして実行します。
cd ~/project
gcc -o typing1b typing1b.c -lcurses
./typing1a

複数の文字列を入力する
次に、前のプログラムを拡張して、プレイヤーが複数の文字列を入力する練習ができるようにしましょう。
~/project ディレクトリに移動し、typing2a.c という名前のプロジェクトファイルを作成します。
cd ~/project
touch typing2a.c
では、プログラムを見てみましょう。コードは以下の通りです。
#include <time.h>
#include <stdio.h>
#include <string.h>
#include "getputch.h"
#define QNO 12 /* 問題の数 */
int main(void)
{
char *str[QNO] = {"book", "computer", "default", "comfort",
"monday", "power", "light", "music",
"programming", "dog", "video", "include"};
int i, stage;
init_getputch();
printf("Start typing practice.\n");
printf("Press the spacebar to begin.\n");
while (getch()!= ' ') /* 待機するまで */
; /* プレイヤーがスペースバーを押す */
for (stage = 0; stage < QNO; stage++) {
int len = strlen(str[stage]); /* 文字列 str[stage] の文字数 */
for (i = 0; i < len; i++) {
/* str[stage][i] 以降の文字を表示し、カーソルを先頭に戻す */
printf("%s \r", &str[stage][i]);
fflush(stdout);
while (getch()!= str[stage][i])
;
}
}
term_getputch();
return 0;
}
このプログラムでは、文字列を入力した後、次の文字列が同じ行に表示され、プレイヤーが入力できるようになります。合計 12 の文字列が練習に利用できます。
このプログラムは主に前のプログラムをベースにしていますが、いくつかの違いがあります。
for文がネストされています。
- 問題の単語数が 1 から 12 に変わったため、外側の
for文が追加されました。このfor文は、変数stageについて 0 から始めてQNO回繰り返します。strlen(str[stage])の後の内側のforループは、前のプログラムのforループと同等です。 - 各反復で入力する文字列は
str[stage](前のプログラムのstrに相当)です。入力する文字数は文字列によって異なるため、strlen(str[stage])文は問題に使用する文字列str[stage]の長さを計算し、変数lenに格納します。
- 入力する文字はもはや
str[i]ではなく、str[stage][i]です。
内側の for ループでは、入力する文字は str[stage][i] で、これは前のプログラムの str[i] に相当します。
以下のコマンドを使用してコンパイルして実行します。
cd ~/project
gcc -o typing2a typing2a.c -lcurses
./typing2a

質問の順序をシャッフルする(方法 1)
前のプログラムで数回練習した後、問題で次に表示される文字列が自動的に頭に浮かんでしまい、トレーニング効果が薄れます。では、問題の順序をシャッフルしましょう。
~/project ディレクトリに移動し、プロジェクトファイル typing2b.c を作成します。
cd ~/project
touch typing2b.c
では、プログラムを見てみましょう。コードは以下の通りです。
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getputch.h"
#define QNO 12 /* 問題の数 */
#define swap(type, x, y) do { type t = x; x = y; y = t; } while (0)
int main(void)
{
char *str[QNO] = {"book", "computer", "default", "comfort",
"monday", "power", "light", "music",
"programming", "dog", "video", "include"};
int i, stage;
int qno[QNO]; /* 問題の順序 */
init_getputch();
srand(time(NULL)); /* 乱数のシードを設定 */
for (i = 0; i < QNO; i++)
qno[i] = i;
for (i = QNO - 1; i > 0; i--) {
int j = rand() % (i + 1);
if (i!= j)
swap(int, qno[i], qno[j]);
}
printf("Start typing practice.\n");
printf("Press the spacebar to begin.\n");
while (getch()!= ' ') /* 待機するまで */
; /* プレイヤーがスペースバーを押す */
for (stage = 0; stage < QNO; stage++) {
int len = strlen(str[qno[stage]]); /* 文字列 str[qno[stage]] の文字数 */
for (i = 0; i < len; i++) {
/* str[qno[stage]][i] 以降の文字を表示し、カーソルを先頭に戻す */
printf("%s \r", &str[qno[stage]][i]);
fflush(stdout);
while (getch()!= str[qno[stage]][i])
;
}
}
term_getputch();
return 0;
}
問題の順序をシャッフルするために、このプログラムでは qno という新しい配列を導入しています。この配列の要素の型は int で、サイズは QNO(問題の文字列の数、つまり 12)です。
タイピング練習を開始する前に、最初の 2 つの for ループを使って、配列 qno の各要素に 0, 1, 2,..., 11 の順で値を割り当てます。
前のプログラムと同様に、str[stage] がすべて str[qno[stage]] に置き換えられています。これは、このプログラムの各ループで出題される問題が str[qno[stage]] に基づいているからです。
stageが 0 のとき、qno[0]の値が 2 なので、プログラムが表示する問題はstr[2]、つまりdefaultになります。stageが 1 のとき、qno[1]の値が 1 なので、プログラムが表示する問題はstr[1]、つまりcomputerになります。
以下同様です。12 の文字列で練習した後、プログラムは終了します。
以下のコマンドを使用してコンパイルして実行します。
cd ~/project
gcc -o typing2b typing2b.c -lcurses
./typing2b

質問の順序をシャッフルする(方法 2)
以下のプログラムでは、配列を使用せずに別の方法で問題の順序をシャッフルします。このプログラムは、前のプログラムと比較して必要な変数が少なく、より簡潔です。
~/project ディレクトリに移動し、プロジェクトファイル typing2c.c を作成します。
cd ~/project
touch typing2c.c
では、プログラムを見てみましょう。コードは以下の通りです。
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getputch.h"
#define QNO 12 /* 問題の数 */
#define swap(type, x, y) do { type t = x; x = y; y = t; } while (0)
int main(void)
{
char *str[QNO] = {"book", "computer", "default", "comfort",
"monday", "power", "light", "music",
"programming", "dog", "video", "include"};
int i, stage;
init_getputch();
srand(time(NULL)); /* 乱数のシードを設定 */
for (i = QNO - 1; i > 0; i--) { /* 配列 str をシャッフル */
int j = rand() % (i + 1);
if (i!= j)
swap(char *, str[i], str[j]);
}
printf("Start Typing Practice.\n");
printf("Press the spacebar to begin.\n");
while (getch()!= ' ') /* 待機するまで */
; /* プレイヤーがスペースバーを押す */
for (stage = 0; stage < QNO; stage++) {
int len = strlen(str[stage]); /* 文字列 str[stage] の文字数 */
for (i = 0; i < len; i++) {
/* str[stage][i] 以降の文字を表示し、カーソルを先頭に戻す */
printf("%s \r", &str[stage][i]);
fflush(stdout);
while (getch()!= str[stage][i])
;
}
}
term_getputch();
return 0;
}
配列 str の各要素は、文字列 book, computer, default,... を指しています。str[0] と str[2] の値を交換すると、str[0] は default を指し、str[2] は book を指すようになります。
プログラム(上記のコード)の最初の for ループは、配列 str の要素の値であるポインタを並べ替える役割を担っており、これにより要素がシャッフルされます。交換するオブジェクトがポインタであるため、関数マクロ swap に渡される最初の引数は char * です。
プログラムのメイン部分の 2 番目の for ループでは、各段階の問題は str[stage](リスト 8 - 4 では str[qno[stage]] となっており、より複雑です)に戻されます。これは、配列 str がランダムに並べ替えられており、str[0], str[1],..., str[QNO - 1] を順に出力することで、ランダムな問題の順序が得られるからです。
この方法の 1 つの欠点は、単語の順序をシャッフルすると、元に戻すことができない ことです。この点にはご留意ください。
以下のコマンドを使用してコンパイルして実行します。
cd ~/project
gcc -o typing2c typing2c.c -lcurses
./typing2c

キーボードレイアウトの連想タイピング
次に、プレイヤーがキーボード上の各キーの位置を思い出しながらタイピングの練習ができるソフトウェアを開発します。通常のタイピング練習とは異なり、プレイヤーは何のプロンプトもなしに文字を入力する必要があります。また、キーボードのレイアウトはそれぞれ異なり、ここでは以下の図に示すキーボードレイアウトを基準として使用します。


図に示すキーボードレイアウトでは、[Shift] キーを押しながら [0] キーを押しても、何の情報も入力されません。
このキーボードのレイアウトについて、以下の点を見ることができます。
- キーは 4 層から構成されています。
- 各層は左手用キーと右手用キーに分かれており、左手は黒いキーを、右手は青いキーを打ちます。
[Shift]キーを押さずに打つキーと、[Shift]キーを押しながら打つキーがあります。
層/手/[Shift] キーの押下の有無で分類される様々なセットを「ブロック」と呼び、キーボード全体は合計 4 × 2 × 2 = 16 のブロックから構成されています。
例えば、3 層目で [Shift] キーを押しながら左手で打つキーのブロックは [A]、[S]、[D]、[F]、[G](それぞれ小指、薬指、中指、人差し指、人差し指で操作)です。
このトレーニングソフトウェアでは、ブロックが問題として提示されますが、ソフトウェアはこのブロック内の 1 文字を「?」で隠します。例えば、以下の問題では、プレイヤーは隠された「?」を大文字の D と関連付け、その文字を入力する必要があります。
A S? F G
コードと実行結果
~/project ディレクトリに移動し、typing3.c という名前のプロジェクトファイルを作成します。
cd ~/project
touch typing3.c
上記のガイドラインに従って書かれたプログラムは以下の通りです。
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getputch.h"
#define NO 30 /* 練習の回数 */
#define KTYPE 16 /* ブロックの数 */
int main(void)
{
char *kstr[] = {"12345", "67890-^\\", /* 第 1 層 */
"!\"#$%", "&'() =~|", /* 第 1 層 [Shift] */
"qwert", "yuiop@[", /* 第 2 層 */
"QWERT", "YUIOP`{", /* 第 2 層 [Shift] */
"asdfg", "hjkl;:]", /* 第 3 層 */
"ASDFG", "HJKL+*}", /* 第 3 層 [Shift] */
"zxcvb", "nm,./\\", /* 第 4 層 */
"ZXCVB", "NM<> _", /* 第 4 層 [Shift] */
};
int i, stage;
clock_t start, end; /* 開始と終了時間 */
init_getputch();
srand(time(NULL)); /* 乱数生成のシードを設定 */
printf("Start typing association exercises.\n");
printf("Please enter the hidden character indicated by '?'.\n");
printf("Press the space key to start.\n");
fflush(stdout);
while (getch()!= ' ')
;
start = clock(); /* 開始時間 */
for (stage = 0; stage < NO; stage++) {
int k, p, key;
char temp[10];
do {
k = rand() % KTYPE;
p = rand() % strlen(kstr[k]);
key = kstr[k][p];
} while (key == ' ');
strcpy(temp, kstr[k]);
temp[p] = '?';
printf("%s", temp);
fflush(stdout);
while (getch()!= key)
;
putchar('\n');
}
end = clock(); /* 終了時間 */
printf("Time taken: %.1f seconds.\n", (double)(end - start) / CLOCKS_PER_SEC);
term_getputch();
return 0;
}
マクロ KTYPE はブロックの数を表し、16 です。配列 kstr は、各ブロックの文字を左から右に並べた文字列を格納するために使用されます。
トレーニングの目的上、問題には文字 ? が含まれないため、宣言されたブロックの最後の文字列は NM<> _ であり、NM<>?_ ではありません(プログラムはスペースキーを使って問題を生成しないため、エラーは発生しません)。
あなたのキーボードレイアウトがこの例に示されたものと異なる場合は、kstr 配列の宣言を適宜変更してください。
最初の do while ループは問題を生成する役割を担っています。
- 変数
kは、どのブロックから問題を生成するかを示します。この値はkstr配列のインデックスに対応するため、0 以上KTYPE未満の乱数として設定されます。ブロックの数
KTYPEが 16 であるため、生成される乱数の範囲は 0 から 15 となります。 - 変数
pは、ブロック内のどの文字を隠して問題を生成するかを示します。この値は、ブロック内で問題生成に使用される文字列のインデックスに対応するため、0 以上ブロック内の文字数未満の乱数として設定されます。仮に
kが 0 の場合、ブロックは'12345'という 5 文字から構成されるため、pは 0 から 4 の範囲の乱数として設定されます。また、kが 3 の場合、ブロックは'&'()=~|'という 8 文字から構成されるため、pは 0 から 7 の範囲の乱数として設定されます。 - 変数
keyは、隠された文字を表します。
例えば、k が 0 で p が 2 の場合、ブロック '12345' 内の文字 '3' が key となります。プログラムは問題生成に使用すべきでない文字にスペース文字 '' を割り当てているため、隠された文字 key がスペース文字の場合、do - while ループを使って問題を再生成します。
次に、strcpy 関数が kstr[k] を temp にコピーし、temp[p] に '?' を代入します。これにより、画面に表示する文字列 12?45 が生成されます。
プログラムが文字列 temp を表示し、キーボードから入力された文字 key を読み取ることができれば正しいです。前のタイピング練習プログラムと同様に、このプログラムは誤って入力された文字を受け付けません。30 回のトレーニングラウンドの後、プログラムは実行を終了します。
以下のコマンドを使用してコンパイルして実行します。
cd ~/project
gcc -o typing3 typing3.c -lcurses
./typing3
Start typing association exercises.
Please enter the hidden character indicated by '?'.
Press the space key to start.
AS?FG
?m,./\
67890-?\
?XCVB
zx?vb
!"?$%
ZXC?B
hjk?;:]
…(省略)…
まとめ
このプロジェクトでは、C プログラミング言語を使用してタイピング練習プログラムを作成する方法を学びました。このタイピング練習プログラムは、コーディングや一般的なコミュニケーションのいずれの場面でも、ユーザーに多様なインタラクティブなプラットフォームを提供し、タイピングスキルを向上させることができます。これは、タイピングの速度と正確性を向上させるための貴重なツールとなります。



