Тема лабораторной
В этом упражнении вы познакомитесь со строками в языке Си. Вы также узнаете о новых библиотеках, предназначенных для работы со строками, и опробуете их различные функции. Позже мы создадим забавный инструмент для поиска анаграмм.
Цели
- Понять, как работают массивы и строки в языке 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?
- Как определить размер поля