Программа для практики набора текста на языке C

CCBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом проекте вы научитесь создавать программу для практики набору текста на языке программирования 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 для продвинутых операций ввода и вывода на экран.

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL c(("C")) -.-> c/CompoundTypesGroup(["Compound Types"]) c(("C")) -.-> c/PointersandMemoryGroup(["Pointers and Memory"]) c(("C")) -.-> c/UserInteractionGroup(["User Interaction"]) c(("C")) -.-> c/BasicsGroup(["Basics"]) c(("C")) -.-> c/ControlFlowGroup(["Control Flow"]) c/BasicsGroup -.-> c/operators("Operators") c/ControlFlowGroup -.-> c/if_else("If...Else") c/ControlFlowGroup -.-> c/for_loop("For Loop") c/ControlFlowGroup -.-> c/while_loop("While Loop") c/CompoundTypesGroup -.-> c/arrays("Arrays") c/CompoundTypesGroup -.-> c/strings("Strings") c/PointersandMemoryGroup -.-> c/pointers("Pointers") c/UserInteractionGroup -.-> c/user_input("User Input") subgraph Lab Skills c/operators -.-> lab-298834{{"Программа для практики набора текста на языке C"}} c/if_else -.-> lab-298834{{"Программа для практики набора текста на языке C"}} c/for_loop -.-> lab-298834{{"Программа для практики набора текста на языке C"}} c/while_loop -.-> lab-298834{{"Программа для практики набора текста на языке C"}} c/arrays -.-> lab-298834{{"Программа для практики набора текста на языке C"}} c/strings -.-> lab-298834{{"Программа для практики набора текста на языке C"}} c/pointers -.-> lab-298834{{"Программа для практики набора текста на языке C"}} c/user_input -.-> lab-298834{{"Программа для практики набора текста на языке C"}} end

Проектная среда

Для проекта требуется использование библиотеки 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?";     /* The string to be input */
    int    len = strlen(str);           /* Number of characters in the string */

    init_getputch();
    printf("Please type as shown.\n");
    printf("%s\n", str);                /* Display the string to be input */
    fflush(stdout);

    for (i = 0; i < len; i++) {
        int ch;
        do {
            ch = getch();                 /* Read input from the keyboard */
            if (isprint(ch)) {
                putch(ch);                /* Display the pressed key */
                if (ch!= str[i])         /* If a wrong key is pressed */
                    putch('\b');          /* Move the cursor back one space */
            }
        } 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', чтобы переместить курсор на одну позицию назад. Это гарантирует, что следующий введенный символ будет отображен в той же позиции.

После выполнения вышеуказанных шагов вычисляется управляющее выражение цикла do (ch!= str[i]). Когда вводится неверный символ (когда ch не равен str[i]), цикл do запускается снова. В этот момент программа не переходит к следующему символу, а снова выполняет соответствующую часть в цикле do... while. После ввода правильного символа значение i увеличивается в цикле for, и программа переходит к следующему символу. После ввода всех символов программа отобразит время, затраченное игроком.

Скомпилируйте и запустите программу с помощью следующих команд:

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?";    /* The string to be entered */
    int     len = strlen(str);          /* The number of characters in the string str */

    init_getputch();
    printf("Please type as shown.\n");

    for (i = 0; i < len; i++) {
        /* Display the characters after str[i] and return the cursor to the beginning */
        printf("%s \r", &str[i]);
        fflush(stdout);
        while (getch()!= str[i])
            ;
    }

    term_getputch();
    return 0;
}

Эта программа немного отличается от предыдущей. Каждый раз, когда вводится правильный символ, один символ исчезает, а остальные символы сдвигаются вперед. Аналогично, программа не переходит к следующему символу, пока игрок не введет правильную клавишу. Когда игрок правильно вводит все символы, все символы исчезнут, и программа завершится.

Хотя операция, выполняемая здесь, более "продвинутая", чем в предыдущей программе, сама программа на самом деле короче. Тело оператора for состоит всего из двух коротких инструкций.

  • &str[i], переданное функции printf в инструкции printf("%s \r", &str[i]);, является указателем на str[i]. Поскольку значение переменной i равно 0, указатель &str[i] будет указывать на символ 'H', в результате чего на экране отобразится строка How do you do?, начиная с str[0], как показано на рисунке выше. Затем программа выведет пробельные символы и возврат каретки \r сразу после этой строки и вернет курсор в позицию символа 'H' в начале строки.
  • Если введенный символ (возвращаемое значение функции getch) не равен str[i], то есть введенный символ не 'H', оператор while будет выполняться в цикле до тех пор, пока игрок не введет правильный символ, после чего оператор while завершится.
  • Затем значение переменной i станет равным 1 под влиянием оператора for. Как показано на рисунке выше, инструкция printf("%s \r", &str[i]); выведет строку ow do you do?, начиная с str[1], затем выведет пробельные символы и возврат каретки и вернет курсор в позицию символа '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      /* Number of questions */

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()!= ' ')          /* Wait until */
        ;                           /* the player presses the spacebar */

    for (stage = 0; stage < QNO; stage++) {
        int len = strlen(str[stage]);   /* Number of characters in string str[stage] */
        for (i = 0; i < len; i++) {
            /* Display the characters after str[stage][i] and return the cursor to the beginning */
            printf("%s \r", &str[stage][i]);

            fflush(stdout);
            while (getch()!= str[stage][i])
                ;
        }
    }

    term_getputch();

    return 0;
}

В этой программе после ввода одной строки следующая строка отображается на той же строке для ввода игроком. Всего доступно 12 строк для практики.

Эта программа в основном основана на предыдущей, но есть несколько отличий:

  1. Оператор for вложен.
  • Поскольку количество слов в вопросе изменилось с 1 на 12, добавлен внешний оператор for. Этот оператор for выполняет QNO итераций, начиная с 0, для переменной stage. Внутренний цикл for после strlen(str[stage]) эквивалентен циклу for в предыдущей программе.
  • Строка для ввода на каждой итерации - это str[stage] (эквивалентно str в предыдущей программе). Количество символов для ввода зависит от строки, поэтому оператор strlen(str[stage]) вычисляет длину строки str[stage], используемой в вопросе, и сохраняет ее в переменной len.
  1. Символ для ввода теперь не 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      /* Number of questions */

#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];                   /* Order of questions */

    init_getputch();
    srand(time(NULL));              /* Set the seed for random numbers */
    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()!= ' ')          /* Wait until */
        ;                           /* the player presses the spacebar */

    for (stage = 0; stage < QNO; stage++) {
        int len = strlen(str[qno[stage]]); /* Number of characters in the string str[qno[stage]] */
        for (i = 0; i < len; i++) {
            /* Display the characters after str[qno[stage]][i] and return the cursor to the beginning */
            printf("%s \r", &str[qno[stage]][i]);
            fflush(stdout);
            while (getch()!= str[qno[stage]][i])
                ;
        }
    }

    term_getputch();
    return 0;
}

Для перемешивания порядка вопросов программа вводит новый массив с именем qno с типом элементов int и размером QNO (который равен количеству строк в вопросах, то есть 12).

Перед началом практики по набору текста два первых цикла 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   /* Number of Questions */

#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));              /* Set random seed */

    for (i = QNO - 1; i > 0; i--) {  /* Shuffle the array 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()!= ' ')          /* Wait until */
        ;                           /* the player presses the spacebar */

    for (stage = 0; stage < QNO; stage++) {
        int len = strlen(str[stage]);	/* Number of characters in the string str[stage] */
        for (i = 0; i < len; i++) {
            /* Display the characters after str[stage][i] and return the cursor to the beginning */
            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 *.

Во втором цикле for в основном теле программы каждый этап вопросов возвращается к str[stage] (что в листинге 8 - 4 было str[qno[stage]], что было более сложно). Это происходит потому, что массив str был случайным образом переупорядочен, и выводя str[0], str[1],..., str[QNO - 1] в порядке, получается случайный порядок вопросов.

Одна из недостатков этого метода заключается в том, что один раз перемешав порядок слов, его нельзя восстановить. Имейте это в виду.

Скомпилируйте и запустите программу с помощью следующих команд:

cd ~/project
gcc -o typing2c typing2c.c -lcurses
./typing2c
Практика набору текста
✨ Проверить решение и практиковаться

Ассоциативный набор текста по раскладке клавиатуры

Далее мы разработаем программное обеспечение, которое позволит игрокам практиковать набор текста, одновременно вспоминая положение каждой клавиши на клавиатуре. В отличие от обычных упражнений по набору текста, игрокам придется вводить символы без каких-либо подсказок. Кроме того, разные клавиатуры имеют разную раскладку, и здесь мы будем использовать раскладку клавиатуры, показанную на следующем рисунке, в качестве эталона.

Практика набору текста
Практика набору текста

На раскладке клавиатуры, показанной на рисунке, даже если удерживать клавишу [Shift] при нажатии клавиши [0], никакой информация не будет введена.

Что касается раскладки этой клавиатуры, мы можем заметить следующее.

  • Она состоит из 4 слоев клавиш.
  • Каждый слой разделен на клавиши для левой руки и клавиши для правой руки, причем левая рука нажимает на черные клавиши, а правая - на синие.
  • Есть клавиши, которые можно нажимать без удерживания клавиши [Shift], и есть клавиши, которые требуют удерживания клавиши [Shift] при нажатии.

Мы называем различные наборы, классифицированные по слоям/рукам/нажатию клавиши [Shift], "блоками", и вся клавиатура состоит из 4 × 2 × 2 = 16 блоков в общей сложности.

Например, блок клавиш для левой руки с удерживанием клавиши [Shift] на 3-м слое - это [A], [S], [D], [F], [G] (обрабатываемые соответственно мизинцем, безымянным пальцем, средним пальцем, указательным пальцем и указательным пальцем).

В этой обучающей программе в качестве вопроса предлагается блок, но программа скрывает один символ в этом блоке с помощью знака "?". Например, для следующего вопроса игрок должен ассоциировать скрытый знак "?" с заглавной буквой 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			/* Number of exercises */
#define	KTYPE		16			/* Number of blocks */

int main(void)
{
    char *kstr[] = {"12345",  "67890-^\\",  /* Layer 1         */
                    "!\"#$%", "&'() =~|",   /* Layer 1 [Shift] */
                    "qwert",  "yuiop@[",    /* Layer 2         */
                    "QWERT",  "YUIOP`{",    /* Layer 2 [Shift] */
                    "asdfg",  "hjkl;:]",    /* Layer 3         */
                    "ASDFG",  "HJKL+*}",    /* Layer 3 [Shift] */
                    "zxcvb",  "nm,./\\",    /* Layer 4         */
                    "ZXCVB",  "NM<> _",	    /* Layer 4 [Shift] */
                    };
    int i, stage;
    clock_t	start, end;         /* Start and end times */

    init_getputch();
    srand(time(NULL));          /* Set the seed for random number generation */

    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();            /* Start time */

    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();              /* End time */
    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, блок состоит из 5 символов, '12345', поэтому p задается как случайное число в диапазоне от 0 до 4. Кроме того, если k равно 3, блок состоит из 8 символов, '&'()=~|', поэтому p задается как случайное число в диапазоне от 0 до 7.

  • Переменная key представляет скрытый символ.

Например, если k равно 0 и p равно 2, символ '3' в блоке '12345' является key. Поскольку программа уже присвоила пробельный символ '' символам, которые не должны использоваться для генерации вопросов, цикл do-while используется для повторной генерации вопроса, если скрытый символ key является пробельным символом.

Далее функция 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?;:]
…(omit)…
✨ Проверить решение и практиковаться

Итоги

В этом проекте вы научились создавать программу для практики набора текста на языке программирования C. Эта программа для практики набора текста предоставляет пользователям гибкую интерактивную платформу для улучшения навыков набора текста, будь то для программирования или обычного общения. Она представляет собой ценный инструмент для повышения скорости и точности набора текста.