Рядки в Сі. Основні функції.
Рядок це набір символів char, що завершуються нульовим символом закінчення рядка '\0' або 0.
char *name = "Victor"; // Нульовий символ буде вставлено після останньої літери r char name[] = "Victor"; char name[7] = "Victor"; // Необхідно резервувати місце під символ закінчення рядка char name[7] = "Victor\0"; // Рядок з явною вказівкою завершення
Функції роботи з рядками
atof() Перетворює рядок у float.
atoi() Перетворює рядок на int.
atoll() Перетворює рядок у long long.
fgets() Читає рядки з файлу або клавіатури.
gets() Читає рядки з клавіатури.
memccpy() Копіює задану кількість байт або до першого знаходження заданого символу (включаючи його).
memcpy() Копіює задану кількість байт (символів) з src в dst.
memmove() Копіює задану кількість байт (символів) з src в dst.
printf() Виводить текст на екран.
scanf() Перетворює введення з клавіатури на змінні (рядкові, числові і т.д.), відповідно до специфікатора формату.
sprintf_s() Перетворює рядок на тип відповідно до специфікатора формату.
strchr() Шукає символ у рядку і повертає на нього покажчик, якщо не знайдено - NULL.
strcpy() Копіює рядок (включаючи нульовий символ, що завершує рядок) в інший рядок.
strncpy() Копіює рядок (включаючи завершальний рядок нульовий символ) в інший рядок з обмеженням довжини рядка, що копіюється.
strlen() Повертає довжину рядка без кінцевого нульового символу.
strtod() Перетворює рядок у double.
strcat() Здійснює об'єднання двох рядків.
strncat() Здійснює об'єднання двох рядків з обмеженням довжини рядка, що додається.
strdup() Виділяє пам'ять та копіює рядок, повертає адресу скопійованого рядка.
Сподобалася сторінка?
Додати до закладок
Або поділитись!
Введення та виведення символьних рядків у Сі
Отже, рядки у мові Сі. Для них не передбачено окремого типу даних, як це зроблено у багатьох інших мовах програмування. У мові Сі рядок – це масив символів. Для позначення кінця рядка використовується символ '\0' , про який ми говорили в минулій частині цього уроку. На екрані він ніяк не з'являється, тому подивитися на нього не вийде.
Створення та ініціалізація рядка
Оскільки рядок – це масив символів, то оголошення та ініціалізація рядка аналогічні подібним операціям з одновимірними масивами.
Наступний код ілюструє різні способи ініціалізації рядків.
char str [10]; char str1[10] =; char str2[10] = "Hello!"; char str3[] = "Hello!";
Рис.1 Оголошення та ініціалізація рядків
У першому рядку ми просто оголошуємо масив із десяти символів. Це не зовсім рядок, т.к. в ній відсутній нуль-символ \0, поки це просто набір символів.
Другий рядок. Найпростіший спосіб ініціалізації у лоб. Оголошуємо кожен символ окремо. Тут головне не забути додати нуль-символ \0.
Третій рядок – аналог другого рядка. Зверніть увагу на картинку. Т.к. символів у рядку праворуч менше, ніж елементів у масиві, решта елементів заповниться \0 .
Четвертий рядок. Як бачите, тут не встановлено розміру. Програма його обчислить автоматично та створить масив символів необхідної довжини. При цьому останнім буде вставлено нуль-символ 0 .
Як вивести рядок
Доповнимо код вище до повноцінної програми, яка виводитиме створені рядки на екран.
#include int main(void) < char str[10]; char str1[10] =; char str2[10] = "Hello!"; char str3[] = "Hello!"; for(int i = 0; iРис.2 Різні способи виведення рядка на екран
Як бачите, є кілька основних способів вивести рядок на екран.
- використовувати функцію printf зі специфікатором %s
- використовувати функцію puts
- використовувати функцію fputs, вказавши як другий параметр стандартний потік для виведення stdout.
Єдиний нюанс у функцій puts та fputs. Зверніть увагу, що функція puts переносить виведення на наступний рядок, а функція fputs не переносить.
Як бачите, з висновком все досить просто.
Введення рядків
З введенням рядків все трохи складніше, ніж із виводом. Найпростішим способом буде наступне:
#include int main(void)Функція gets припиняє роботу програми, читає рядок символів, введених з клавіатури, і поміщає символьний масив, ім'я якого передається функції як параметр.
Завершенням роботи функції gets буде символ, відповідний клавіші введення та записуваний у рядок як нульовий символ.
Помітили небезпеку? Якщо ні, то вас люб'язно попередить компілятор. Справа в тому, що функція gets завершує роботу лише тоді, коли користувач натискає клавішу введення. Це може призвести до того, що ми можемо вийти за рамки масиву, в нашому випадку — якщо введено більше 20 символів.
До речі, раніше помилки переповнення буфера вважалися найпоширенішим типом уразливості. Вони зустрічаються і зараз, але використовувати їх для злому програм стало набагато складніше.
Отже, що ми маємо. Ми маємо завдання: записати рядок у масив обмеженого розміру. Тобто ми повинні якось контролювати кількість символів, які користувач вводить. І тут нам на допомогу приходить функція fgets:
#include int main(void)Функція fgets приймає на вхід три аргументи: змінну для запису рядка, розмір рядка, що записується, і ім'я потоку, звідки взяти дані для запису в рядок, в даному випадку — stdin . Як ви вже знаєте з трьох уроків, stdin – це стандартний потік введення даних, зазвичай пов'язаний з клавіатурою.Зовсім необов'язково дані повинні надходити саме з потоку stdin, надалі цю функцію ми також використовуватимемо для читання даних із файлів.
Якщо в ході виконання цієї програми ми введемо рядок довше, ніж 10 символів, масив все одно буде записано лише 9 символів з початку і символ перенесення рядка, fgets «обріже» рядок під необхідну довжину.
Зверніть увагу, що функція fgets зчитує не 10 символів, а 9 ! Як ми пам'ятаємо, у рядках останній символ зарезервовано для нуль-символу.
Давайте це перевіримо. Запустимо програму з останнього лістингу. І введемо рядок 1234567890. На екран виведеться рядок 123456789.
Рис.3 Приклад роботи функції fgets
Виникає питання. А куди подівся десятий символ? А я відповім. Він нікуди не подівся, він залишився в потоці введення. Виконайте наведену нижче програму.
#include int main(void)Ось результат роботи.
Рис.4 Непорожній буфер stdin
Поясню те, що сталося. Ми викликали функцію fgets. Вона відкрила потік введення та дочекалася доки ми введемо дані. Ми ввели з клавіатури 1234567890 (n я позначаю натискання клавіша Enter ). Це вирушило в потік введення stdin. Функція fgets, як і належить, взяла з потоку введення перші 9 символів 123456789, додала до них нуль-символ \0 і записала це в рядок str. У потоці введення залишилося ще 0\n.
Далі ми оголошуємо змінну h. Виводимо її значення на екран. Після чого викликаємо функцію scanf. Тут очікується, що ми можемо щось запровадити, але т.к. в потоці введення висить 0\n , то функція scanf сприймає це як наше введення, і записується 0 змінну h . Далі ми виводимо її на екран.
Це, звичайно, не зовсім така поведінка, яку ми очікуємо. Щоб впоратися з цією проблемою, необхідно очистити буфер введення після того, як ми вважали рядок, введений користувачем.Для цього використовується спеціальна функція fflush. Вона має лише один параметр – потік, який потрібно очистити.
Виправимо останній приклад так, щоб його робота була передбачуваною.
#include int main(void) < char str[10]; fgets(str, 10, stdin); fflush(stdin); // Очищаємо потік введення puts (str); int h = 99; printf("do %d\n", h); scanf("%d",&h); printf("posle %d\n", h); return 0; >
Тепер програма працюватиме так, як треба.
Рис.4 Скидання буфера stdin функцією fflush
Підсумовуючи, можна відзначити два факти. Перший. На даний момент використання функції gets є небезпечним, тому рекомендується скрізь використовувати функцію fgets.
Другий. Не забувайте очищати буфер введення, якщо використовуєте функцію fgets.
На цьому розмова про введення рядків закінчена. Ідемо далі.
Збережи в закладки або підтримуй проект.
Практика
Розв'яжіть запропоновані завдання:
Для зручності роботи відразу переходьте в повноекранний режим
Дослідницькі завдання для хакерів
- Перевірте як поводиться компілятор у разі переповнення буфера.
Додаткові матеріали
Запис та зчитування символів та рядків. Частина 1
Доброго часу доби! Сьогодні ми поговоримо про рядки та символи в Сі, і про те, як вони передаються у файл.
Спочатку трохи теорії. До цього моменту ми вже розбирали, як передавати змінну на виведення за допомогою специфікаторів формату (%d, %c і т.д.). А також розглядали різні методи відкриття файлів для запису та читання. Тепер давайте узагальним усі варіанти відкриття файлу і, що означає кожне позначення.
Режими читання та запису файлів
У Сі, для відкриття файлу, використовується команда fopen(), в аргументах якого записується ім'я файлу та позначення режиму запису:
r (r+) - файл відкривається для читання (читання та запису);
w (w+) — відкривається порожній файл для запису (читання та запису). Якщо файл з такою назвою існує, він стирається;
a (а+) — файл відкривається для доповнення до кінця (читання та доповнення).
Як приклад: file = fopen("file.tx) .
Специфікатори
Повторимо основну частину функції scanf():
%c Читає одиночні символи
%d Читає десяткове число
%f Читає число з плаваючою комою
%s Читає рядок
Інші функції зчитування розберемо на прикладах.
Завдання на запис та зчитування рядків та символів у Сі
Тепер, коли ми все узагальнили, приступимо до завдань на символи та рядки.
Записати в текстовий файл на різних рядках 5 слів та 3 числа, закрити його, а потім дописати туди ще 2 символи.
Це нескладне завдання перевіряє нас на знання тих самих режимів відкриття файлів, про які ми говорили раніше.
#include #include int main(void) FILE*file; file = fopen(«file.txt»,»w+»); char str[200]; int ch1, ch2, ch3; gets(str); scanf(«%d%d%d», & ch1,& ch2,& ch3);
Для роботи з рядками Сі передбачені різноманітні бібліотеки з різними функціями. У стандартній бібліотеці список таких функцій скромний, але в більшості випадків цього достатньо. gets() аналог функції scanf(), однак, працює він лише з рядком. Записує в зазначений аргумент рядок, що сканується ним, з консолі введення у форматі символів.
Насправді, і звичайні рядки слід сприймати як звичайні одновимірні масиви з форматом char. Також слід врахувати, що розмір масиву (у нас char str[200]) має бути не менше ніж рядок, а краще більше.
fputs(str, file); fprintf(file,"\n%d, %d, %d", ch1,ch2,ch3); fclose(file);
Зроблено запис у файл зчитаних з консолі рядка та цифр. І закрили файл. ЗАВЖДИ Закрийте файл після відкриття. інакше це може вплинути на працездатність комп'ютера.
file = fopen("file.txt","a+"); char sym1 = 'R', sym2 = 'T'; fprintf(file, " \n %c , %c ", sym1, sym2); fclose(file); return EXIT_SUCCESS; >
Йдемо за завданням і знову відкриваємо файл. Звертаю увагу до режиму відкриття файлу («a+»), записуємо туди 2 символи. І закриваємо файл.
Створити вручну текстовий файл англомовного невеликого обсягу.Прочитати файл символом і вивести на екран усі символи з файлу. Пропустивши рядок, вивести на екран першу та останню літеру кожного рядка у файлі.
Так як має бути складніша робота з рядками, відразу підключимо бібліотеку string.h
#include stdio.h> #include stdlib.h> #include string.h> #define N 200 int main(void) int i = 0; FILE*file; file = fopen("file.txt", "r"); char sym[N], c = 0; while( (c = fgetc(file)) != EOF) printf("%c", c); >
Після відкриття файлу в режимі читання та ініціалізації змінних, йде код, який стане вам у нагоді в майбутньому. Виведення на екран файлу символічно! В умовах while вказано «поки сканований символ не означає кінець файлу». EOF - символ відомий в С++ як \0, означає кінець файлу.
fclose(file); printf("\n"); file = fopen("file.txt", "r"); while( !feof(file)) fscanf(file,"%s",sym); i = (strlen(sym)); printf("%c %c ", sym[0], sym[i-1]); > fclose(file); return EXIT_SUCCESS; >
Умова, що використовується при while, так само як і в попередній статті, однак має більш коротку форму. Функція strlen() з бібліотеки string.h дозволяє визначити довжину рядка. І уявивши, що рядок — одномірний масив, звернемося до конкретних символів через індекс.
Створити вручну текстовий файл англомовного невеликого обсягу. Знайти, скільки всього голосних міститься у цьому файлі. Знайти кількість голосних у кожному рядку файлу. Результати дописати цей же файл на новому рядку.
#include stdio.h> #include stdlib.h> #include string.h> #define N 100 int main(void) int vse = 0, i = 0, k = 0; FILE*file; file = fopen("file.txt", "r"); char c = 0, temp[N]; while( (c = fgetc(file)) != EOF) if ((c == 'A') || (c == 'a') || (c == 'E') || (c == 'e') || (c == 'I') || (c == 'i') || (c == 'O') || (c == 'o') || (c== 'Q') || (c == 'q') || (c == 'U') || (c == 'u') || (c == 'Y') || (c == 'y')) vse++; > fclose(file);
Насамперед, порахуємо кількість голосних у всьому файлі за допомогою лічильника vse.
file = fopen("file.txt", "r"); k = 0; while (!feof(file)) fgets(temp, N, file); k++; > fclose(file);
Тепер порахуємо кількість рядків, щоб створити одномірний масив з кількістю елементів, що дорівнює кількості рядків.
file = fopen("file.txt", "r"); int * kol; int j; kol = (int*)malloc(k * sizeof(int)); for (j = 0; j k; j++) kol[j] = 0; fgets(temp, N ,file); for (i = 0; i (strlen(temp)); i++) if ((temp[i] == 'A') || (temp[i] == 'a') || (temp[i] == 'E') || (temp[i] == 'e') || (temp[i] == 'I') || (temp[i] == 'i') || (temp[i] == 'O') || (temp[i] == 'o') || (temp[i] == 'Q') || (temp[i] == 'q') || (temp[i] == 'U') || (temp[i] == 'u') || (temp[i] == 'Y') || (temp[i] == 'y')) kol[j]++; > > fclose(file);
Не лякайтесь, друзі! Для створення масиву, розмір якого міститься у змінній, використовується команда (int *) malloc (k * sizeof (int)); . Грубо кажучи, ми створили динамічний масив у Сі. У циклі, скануючи по рядку за допомогою fgets() (де temp - Змінна куди записати рядок, N - Кількість символів, file - Назва файлу з яким працюємо), знаходимо в рядку голосні літери. Наприкінці записуємо значення лічильника до нашого масиву.
file = fopen("file.txt", "a+"); fprintf(file,"\n"); for (j = 0; j k; j++) fprintf(file,"%d ", kol[j]); > fprintf(file,"\t vse %d", vse); fclose(file); free(kol); return EXIT_SUCCESS; >
Тепер просто записуємо показання лічильників, що потрібні за завданням, в кінець файлу. Як і у разі закриття файлів, так і при роботі з malloc ЗАВЖДИ звільняйте пам'ять наприкінці програми за допомогою команди free() , де в аргументах ім'я змінної.
На сьогодні все, якщо лишилися питання, пишіть у коментарях. Ось вихідники:
Усі вихідні коди без файлів txt.
Завантажити вихідні завдання — 1
Завантажити вихідні завдання — 2
Завантажити вихідні завдання — 3
