O čom je lab
Na tomto cvičení sa zoznámite s reťazcami v jazyku C. Taktiež spoznáte nové knižnice určené na prácu s reťazcom a vyskúšate si ich jednotlivé funkcie. Neskôr si vytvoríme zábavný nástroj na zisťovanie anagramov.
Ciele
- Pochopiť fungovanie polí a reťazcov v jazyku C.
- Zoznámiť sa s funkciami knižníc
string
actype
. - Osvojiť si prácu s reťazcom a implementáciu vlastných funkcií.
- Zoznámiť sa s operátorom
sizeof()
.
Postup
Krok 1: Štandardný vstup a výstup
V tomto kroku si vyskúšame, ako funguje štandardný vstup a výstup v prípade reťazca. Vyskúšame si rôzne zaužívané spôsoby implementácie.
Úloha 1.1
Vytvorte adresár ~/labs/lab6
, do ktorého budete ukladať svoje kódy programov v jazyku C.
Úloha 1.2
Vo svojom obľúbenom textovom editore si vytvorte nový súbor s názvom my_name.c
.
Úloha 1.3
Vytvorte program, ktorý načíta vstup od používateľa z klávesnice pomocou funkcie scanf()
(s jedným %s
) a vypíše: "Hello, {vaše_meno}". Tentokrát naše meno bude Janko.
Riešením môže byť nasledujúci kód:
#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;
}
Ak ste program implementovali správne, objaví sa výpis "Hello, Janko".
Úloha 1.4
Skompilovaný program spustite znova, ale tentokrát za Vaše meno napíšte: "Janko Hraško".
CHYBA: Aj napriek tomu, že sme napísali "Janko Hraško", náš výstup je rovnaký. Prečo?
Úloha 1.5
Upravte program tak, aby vedel akceptovať aj takzvané biele znaky.
Riešením môže byť nasledujúci kód:
#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;
}
Použili sme funkciu puts()
, ktorá vypíše reťazec a tiež sme použili funkciu fgets()
, ktorá načíta reťazec od používateľa. Výsledok size(buffer)
je v našom prípade 20
. Čo znamená, že maximálna dĺžka reťazca pre vstup je práve 20
.
Náš reťazec bol ale kratší. Ako vie funkcia, na ktorom indexe skončilo meno?
Krok 2: Ukončenie reťazca
Reťazce ako také v jazyku C neexistujú. Sú to v podstate polia znakov.
V tomto kroku si ukážeme, ako a kedy sa ukončuje reťazec v jazyku C. Koniec reťazca určuje špeciálny znak Null alebo hovorovo nazývaný terminátor '\0'
.
Úloha 2.1
Znova vo svojom obľúbenom textovom editore si vytvorte nový súbor s názvom terminator.c
.
Úloha 2.2
Do súboru si skopírujte nasledujúci kód.
#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;
}
Úloha 2.3
Pomocou operátora sizeof()
zistite veľkosť oboch polí v bajtoch a vypíšte ju na štandardný výstup.
Ak ste postupovali správne, veľkosť oboch polí je práve 13 bajtov. Prečo aj pole hello_world
má viditeľne o jeden znak viac?
Krok 3: Anagram Detected
V tomto kroku si vytvoríme nástroj, ktorý by možno trocha pomohol Tomu Hanksovi vo filme The Da Vinci Code pri identifikovaní anagramu. Pri implementácii nástroja budeme používať funkcie z knižníc string
a ctype
, ktoré sú primárne určené na prácu s reťazcom.
Ako by mal nástroj fungovať:
- ak zadáme hello_world a drOllWhole_, nástroj napíše The two strings are anagrams!
- ak zadáme secure a rescue, nástroj napíše The two strings are anagrams!
- ak zadáme Margit a TheFellOmen, nástroj napíše The two strings are not anagrams!
Úloha 3.1
Znova vo svojom obľúbenom textovom editore si vytvorte nový súbor s názvom anagram.c
.
Úloha 3.2
Implementujte načítanie reťazca od používateľa. Podobne ako v predchádzajúcom kroku.
Úloha 3.3
Na štandardný vstup zadajte 2 slová oddelené medzerou.
Riešením by mohol byť nasledujúci fragment kódu:
#include <stdio.h>
#define BUFFER_SIZE 50
int main()
{
char buffer[BUFFER_SIZE];
fgets(buffer, sizeof(buffer), stdin);
//....
return 0;
}
Pre detekciu anagramu uvažujme, ako porovnávať stringy. Jedným z možných spôsobov je ich rovnaké usporiadanie znakov. Keďže znaky v jazyku C sú reprezentované číslami, reťazec si musíme upraviť. Napríklad znak 'C'
má inú hodnotu v ASCII tabuľke ako znak 'c'
, preto je dôležité upraviť všetky alpha znaky na upper alebo lower.
Úloha 3.4
Implementujte funkciu prepare_string()
, ktorá pripraví reťazec na ďalšie spracovanie.
Pomocou funkcie isalpha()
z knižnice ctype
identifikujte všetky alpha znaky a prekonvertujte ich na lower pomocou funkcie tolower()
.
Riešením by mohol byť nasledujúci fragment.
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]);
}
}
}
Úloha 3.5
Rozdeľte upravený string na 2 samotné reťazce podľa medzery a uložte ich zvlášť do premenných. Môžete použiť funkciu strtok()
, ktorá vytvorí takzvaný token na základe nejakého delimetera - v našom prípade medzery. Čiže v podstate rozdelí reťazec.
Riešením by bol nasledujúci fragment kódu.
// ...
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);
// ...
Úloha 3.6
V jednom slove nám však ostala medzera. Implementujte funkciu remove_spaces()
, ktorá odstráni všetky medzery.
Riešením by mohol byť nasledujúci fragment:
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';
}
Úloha 3.7
Teraz, keď máme 2 samotné upravené stringy, môžeme ich usporiadať. Vytvorte funkciu are_anagrams()
, ktorá usporiada znaky v oboch reťazcoch.
Keďže znaky majú aj podobu čísel, môžeme ich triedenie riešiť pomocou cyklu a vetvenia. Zotriedenie takého reťazca predstavuje nasledujúci fragment kódu.
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;
}
}
}
Úloha 3.8
Doplňte funkciu are_anagrams()
tak, aby vedela na konci oba reťazce porovnať a vyhodnotiť výsledok. Na porovnávanie reťazcov môžete použiť funkciu strcmp()
z knižnice string
.
Úloha 3.9
Otestuje svoje riešenie.
Doplňujúce úlohy
Úloha A.1
Upravte program tak, aby algoritmus anagramu nebral do úvahy počet opakovaní znakov reťazca.
Doplňujúce zdroje
- Operátor
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?
- Ako zistiť veľkosť poľa