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
stringactype. - 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 sizeof(buffer) je v našom prípade 20. Čo znamená, že maximálna dĺžka reťazca pre vstup je práve 20 (posledný prvok poľa je alokovaný pre znak '\0').
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