Тема лабораторной
В этом упражнении вы познакомитесь со строками в языке Си. Вы также узнаете о новых библиотеках, предназначенных для работы со строками, и опробуете их различные функции. Позже мы создадим забавный инструмент для поиска анаграмм.
Цели
- Понять, как работают массивы и строки в языке C.
- Изучить функции библиотек
stringиctype. - Научиться работать со строками и реализовывать свои функции.
- Познакомиться с оператором
sizeof().
Инструкции
Шаг 1: Стандартный ввод и вывод
На этом шаге мы проверим, как работает стандартный ввод и вывод в отношении строки. Попробуем различные распространенные способы их реализации.
Задача 1.1
Создайте каталог ~/labs/lab6, в котором будет храниться код вашей программы на языке Си.
Задача 1.2
В своем любимом текстовом редакторе создайте новый файл с именем my_name.c.
Задача 1.3
Создайте программу, которая получает ввод пользователя с клавиатуры с помощью функции scanf() (с одним %s) и печатает: "Hello, {ваше_имя}". В данном случае наше имя будет Janko.
Решением может быть следующий код:
#include <stdio.h>
#define BUFFER_SIZE 20
int main()
{
char buffer[BUFFER_SIZE];
printf("Enter name: ");
scanf("%s", buffer);
printf("Hello, %s ", buffer);
return 0;
}
Если вы правильно реализовали программу, то увидите сообщение "Hello, Janko".
Задача 1.4
Снова запустите скомпилированную программу, но на этот раз введите свое новое имя: "Janko Hraško".
ОШИБКА: Даже если мы написали "Janko Hraško ", вывод будет одинаковым. Почему?
Задача 1.5
Измените программу таким образом, чтобы она могла принимать пробелы.
Решением проблемы может стать следующий код:
#include <stdio.h>
#define BUFFER_SIZE 20
int main()
{
char buffer[BUFFER_SIZE];
printf("Enter name: ");
fgets(buffer, sizeof(buffer), stdin); // read string
printf("Hello, ");
puts(buffer); // display string
return 0;
}
Мы использовали функцию puts(), которая выводит строку, а также функцию fgets(), которая получает строку от пользователя. Результат size(buffer) в нашем случае равен 20. Это означает, что максимальная длина входной строки составляет всего 20.
Но ведь наша строка была короче. Как функция узнает, на каком индексе закончилось имя?
Шаг 2: Окончание цепи
Строк как таковых в языке Си не существует. По сути, они представляют собой массивы символов.
В этом шаге мы покажем, как и когда можно завершить строку в языке C. Конец строки определяется специальным символом Null, который в народе называют терминатором '\0'.
Задача 2.1
Снова в любимом текстовом редакторе создайте новый файл с именем terminator.c.
Задача 2.2
Скопируйте следующий код в свой файл.
#include <stdio.h>
int main()
{
char hello_world[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0'};
char hello_world2[] = "Hello World!";
return 0;
}
Задача 2.3
С помощью оператора sizeof() найдите размер обоих строк в байтах и выведите его на стандартный вывод.
Если вы правильно выполнили процедуру, то размер обоих полей составляет всего 13 байт. Почему же тогда строка hello_world также имеет на один символ больше?
Шаг 3: Обнаружение анаграмм
В этом шаге мы создадим инструмент, который мог бы немного помочь Тому Хэнксу в фильме "Код да Винчи" в определении анаграммы. Для реализации инструмента мы будем использовать функции из библиотек string и ctype, которые в основном предназначены для работы со строками.
Как должен работать инструмент:
- Если набрать hello_world и drOllWhole_, то инструмент напишет The two strings are anagrams!
- Если набрать secure и rescue, то инструмент напишет The two strings are anagrams!
- Если мы напечатаем Margit и TheFellOmen, то инструмент напишет The two strings are not anagrams!
Задача 3.1
Снова в любимом текстовом редакторе создайте новый файл с именем anagram.c.
Задача 3.2
Реализуйте получение строки, заданной пользователем. Аналогично предыдущему шагу.
Задача 3.3
В стандартный ввод введите 2 слова, разделенные пробелом.
Решением может стать следующий фрагмент кода:
#include <stdio.h>
#define BUFFER_SIZE 50
int main()
{
char buffer[BUFFER_SIZE];
fgets(buffer, sizeof(buffer), stdin);
//....
return 0;
}
Для обнаружения анаграмм рассмотрим, как сравнивать строки. Одним из возможных способов является их одинаковый символьный порядок. Поскольку символы в языке Си представляются числами, нам необходимо упорядочить строку. Например, символ 'C' имеет в таблице ASCII другое значение, чем символ 'c', поэтому необходимо изменить все символы alpha на upper или lower.
Задача 3.4
Реализовать функцию prepare_string() для подготовки строки к дальнейшей обработке.
Используйте функцию isalpha() из библиотеки ctype для определения всех символов alpha и преобразования их в lower с помощью функции tolower().
Решением может служить следующий фрагмент.
void prepare_string(char* input) {
if (input == NULL)
{
return;
}
int len = strlen(input);
for (int i = 0; i < len; i++)
{
if (isalpha(input[i]))
{
input[i] = tolower(input[i]);
}
}
}
Задача 3.5
Разделите модифицированную строку на две разных в соответствии с пробелом и сохраните их в отдельных переменных. Для этого можно использовать функцию string(), которая создает так называемый токен на основе некоторого разделителя - в нашем случае пробела. То есть, по сути, она разделяет строку на основе знака.
Решением будет следующий фрагмент кода.
// ...
prepare_string(buffer);
char delim[] = " "; // space as delimiter
char *token = strtok(buffer, delim); // first word
char first_part[strlen(token) + 1];
strcpy(first_part, token); // terminator is added by strcpy
token = strtok(NULL, delim); // second word
char second_part[strlen(token) + 1];
strcpy(second_part, token);
// ...
Задача 3.6
Однако в одном слове остался пробел. Используйте функцию remove_spaces() для удаления всех пробелов.
Следующий фрагмент может стать решением :
void remove_spaces(char *str) {
int count = 0;
for (int i = 0; str[i]; i++) {
if (str[i] != ' ' && str[i] != '\t' && str[i] != '\n')
{
str[count++] = str[i];
}
}
str[count] = '\0';
}
Задача 3.7
Теперь, когда у нас есть обе модифицированные строки, мы можем их упорядочить. Создадим функцию are_anagrams(), которая упорядочивает символы в обеих строках.
Поскольку символы также имеют вид чисел, то их сортировку можно решить с помощью циклов и ветвлений. Сортировка такой строки представлена следующим фрагментом кода.
for (int i = 0; i < len1 - 1; i++)
{
for (int j = i + 1; j < len1; j++)
{
if (sorted_str1[i] > sorted_str1[j])
{
char temp = sorted_str1[i];
sorted_str1[i] = sorted_str1[j];
sorted_str1[j] = temp;
}
}
}
Задача 3.8
Добавьте функцию are_anagrams(), чтобы она могла сравнивать две строки в конце и оценивать результат. Для сравнения строк можно использовать функцию strcmp() из библиотеки string.
Задача 3.9
Одтестируйте свое решение.
Дополнительные задачи
Задача A.1
Измените программу так, чтобы алгоритм анаграммы не учитывал количество повторов символов.
Дополнительные источники
- Оператор
sizeof(): c-reference - Queries size of the object or type. Used when actual size of the object must be known. - How does the strtok function in C work?
- Как определить размер поля