Lekce 8 - Textové řetězce v jazyce C
V předešlém cvičení, Řešené úlohy k 7. lekci Céčka, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
Až doposud jsme se v našem kurzu programovacího jazyka C úspěšně vyhýbali textovým řetězcům a pracovali jsme pouze s čísly a znaky. V naprosté většině reálných aplikací ovšem figurují víceznakové texty. Těm se v programování říká textové řetězce (jelikož se jedná o řetěz znaků) nebo někdy jen řetězce. Důvodem odložení tohoto tématu je, že jazyk C jako nízký jazyk žádný datový typ pro řetězce nemá a vlastně s nimi téměř nepočítá. S textem však v céčku můžeme běžně pracovat, jen je to komplikovanější.
Pole znaků
S řetězci lze v céčku pracovat několika způsoby. My si v tomto dílu
uvedeme zatím ten nejjednodušší, tzv. statický řetězec, kterým je pole
char
ů. Když budeme chtít uložit do proměnné textový
řetězec "itnetwork"
, potřebujeme v paměti vytvořit
následující pole typu char
(znak):
'i' | 't' | 'n' | 'e' | 't' | 'w' | 'o' | 'r' | 'k' | '\0' |
V každé přihrádce pole je uložený jeden znak. Všimněte si ovšem, že
poslední přihrádka je navíc a obsahuje znak \0
, což je tzv.
nulový znak. Tím musí končit všechny textové řetězce.
Ačkoli totiž céčko jako jazyk textové řetězce nepodporuje, obsahuje
standardní knihovny, které s nimi pracují. A proto musíme řetězce ukládat
tak, jak se předpokládá, že budou vypadat. Pole s řetězcem tedy musí být
vždy o 1
delší, než je délka textu, který do něj
vkládáme!.
Pozn.: Ačkoli je to nad rámec této lekce, uveďme si, že nulový znak je na konci řetězce z toho důvodu, abychom poznali, kde řetězec končí. Kromě statických polí můžeme totiž řetězce ukládat pomocí ukazatelů jako libovolně dlouhé úseky v paměti, kde se bez této berličky neobejdeme. Tento způsob si ukážeme dále v seriálu. Druhým způsobem, jak označit konec řetězce, je uložit jeho délku před první znak. Tento systém se používal např. v jazyce Pascal, mnohem častěji se však používá ukončení nulovým znakem.
Vytvořme si jednoduchý příklad. Do proměnné si uložme nějaký text a ten následně vypišme do konzole:
{C_CONSOLE}
char text[5] = {'d', 'u', 'h', 'a', '\0'};
printf("%s", text);
{/C_CONSOLE}
Výsledek:
Konzolová aplikace
duha
Dobrá zpráva je, že jazyk C nám umožňuje zadávat text v uvozovkách,
který následně nahradí tzv. řetězcovou konstantou (polem
char
ů, zakončeným znakem \0
). Kód výše můžeme
přepsat na tuto podobu:
{C_CONSOLE}
char text[5] = "duha";
printf("%s", text);
{/C_CONSOLE}
Všimněte si, že pole musí mít stále délku 5
, i když má
duha 4
písmena. Když bude ještě delší, nebude to vadit.
Dokonce na céčku můžeme nechat zjištění délky textu:
{C_CONSOLE}
char text[] = "duha";
printf("%s", text);
{/C_CONSOLE}
Co již bohužel nefunguje je přiřazení řetězcové konstanty do již existujícího pole:
char text[5]; text = "duha"; // Tento řádek způsobí chybu printf("%s", text);
Je to kvůli tomu, že nelze přiřadit pole do pole. Nic nám ovšem nebrání přiřadit jednotlivé znaky pomocí cyklu nebo použít funkce pro kopírování řetězců, viz. dále.
Práce s jednotlivými znaky
S řetězcem můžeme zacházet úplně stejně jako s polem, protože polem
je Není tedy problém vypsat
např. 1. znak nebo jej změnit, případně řetězec zkrátit:
{C_CONSOLE}
char text[] = "duha";
text[0] = 'h';
text[3] = '\0';
printf("%s", text);
{/C_CONSOLE}
Výsledek:
Konzolová aplikace
huh
Změnou čtvrtého znaku na \0
jsme docílili ukončení
řetězce před tímto znakem. Na nulový znak si dejte při editaci znaků
řetězce pozor, když na něj zapomenete, program nebude vědět kde řetězec
končí a dostanete se do paměti, která vám nepatří.
Čtení/výpis řetězce
Řetězce můžeme jednoduše načítat/vypisovat jako jsme byli zvyklí
doposud, použijeme k tomu formátovací sekvenci %s
. Proměnnou
pro řetězec založíme jako pole char
ů a určíme si nějakou
maximální velikost, např. 50 znaků (což je velikost 51
). U
parametrů typu %s
vypouštíme před proměnnou znak
&
, jelikož s polem předáváme rovnou jeho adresu.
Následující program si nechá zadat vaše jméno a následně vás pozdraví:
{C_CONSOLE}
printf("Zadej své jméno: ");
char jmeno[51];
scanf("%50s", jmeno);
printf("Ahoj uživateli %s, vítám tě!", jmeno);
{/C_CONSOLE}
Všimněte si, že ve formátovacím řetězci funkce scanf()
je
uvedena i maximální délka načítaného řetězce. Když bychom ji nezadali a
natrefili na exotického uživatele nebo jen na záškodníka, došlo by k
přetečení pole a rozbití programu.
Funkce scanf()
text bohužel přeruší se zadáním mezery.
Abychom mohli načíst např. Jan Novák"
do jedné
proměnné, upravíme formátovací řetězec ještě tak, aby načítal vše
kromě konce řádku. Upravte si řádku s načítáním do této podoby (mezera
na začátku je opravdu důležitá, protože v bufferu nenechá bílé
znaky):
{C_CONSOLE}
printf("Zadej své jméno: ");
char jmeno[51];
scanf(" %50[^\n]s", jmeno);
printf("Ahoj uživateli %s, vítám tě!", jmeno);
{/C_CONSOLE}
Někdy můžete při načítání textu z konzole narazit na použití
funkcí gets()
nebo fgets()
. Funkci
gets()
se vyhněte, jelikož neumožňuje omezit délku
načítaného řetězce a fgets()
se musí přesměrovat na
standardní vstup. Se scanf()
si bohatě vystačíme.
Standardní funkce pro práci s řetězci
Specifikace jazyka C nám poskytuje mnoho připravených funkcí pro práci s
řetězci, které zjednoduší naše programy. Pro práci s nimi musíme na
začátek souboru přidat vložení hlavičkového souboru
string.h
:
#include <string.h>
Pozn.: Protože jsou funkce pojmenovány pomocí zkratek, uvádím vždy i z čeho název vychází pro lepší zapamatování.
strlen()
- STRing LENgth
Délku řetězce můžeme zjistit pomocí strlen()
. Jedná se o
délku viditelné části bez znaku \0
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
printf("%d", strlen("duha")); // vrátí 4
return (EXIT_SUCCESS);
}
strcat()
- STRing
conCATenate
2 textové řetězce můžeme spojit do jednoho pomocí funkce
strcat()
. Dejte pozor, aby byl v prvním řetězci dostatek
místa.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char text[20] = "duha";
strcat(text, " je na nebi"); // uloží do text "duha je na nebi"
printf("%s", text);
return (EXIT_SUCCESS);
}
strcpy()
- STRing CoPY
Jelikož pole nelze jednoduše celá kopírovat, je nám poskytnuta tato funkce, která naklonuje textový řetězec do jiné proměnné.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char text[5];
strcpy(text, "duha");
printf("%s", text);
return (EXIT_SUCCESS);
}
strchr()
- STRing CHaR
V textu si můžeme nechat vyhledat nějaký znak. Céčko ho od začátku do
konce prohledá a pokud znak nalezne, vrátí na něj tzv. ukazatel. I když ty
ještě neumíme, bude nám stačit, že když od této hodnoty odečteme
řetězec, získáme pozici, na které se znak nachází. Pokud text znak
neobsahuje, získáme hodnotu NULL
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv) {
char text[] = "Pan X znovu udeřil.";
char *p = strchr(text, 'X'); // Zjistíme pozici znaku 'X' v textu
int pozice = p - text;
if (p != NULL)
{
printf("Nalezeno na pozici %d", pozice);
}
else
{
printf("Nenalezeno");
}
return (EXIT_SUCCESS);
}
Asi vás nepřekvapí, že se pozice indexuje od nuly.
strstr()
- STRing subSTRing
Úplně stejně, jako můžeme vyhledat jeden znak, můžeme vyhledat i
řetězec v řetězci. O tom dále hovoříme jako o tzv. podřetězci. Funkce
se používá analogicky s funkcí strchr()
.
strcmp()
- STRing CoMPare
Porovná 2 řetězce podle abecedy a vrátí záporné číslo pokud je
první před druhým, 0
pokud jsou stejné a kladné číslo pokud
je první za druhým.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
printf("%d", strcmp("akát", "blýskavice")); // vrátí záporné číslo
return (EXIT_SUCCESS);
}
Funkce pro práci s řetězci můžeme nalézt také v dalších variantách.
Pokud chceme, aby céčko pracovalo s řetězcem odzadu (např. vyhledávalo od
konce), vyskytuje se v názvu funkce písmeno r
(jako reverse).
Takovou funkcí je např. strrchr()
. Další varianty funkcí mají
v názvu navíc písmeno n
(jako number) a přijímají navíc
další parametr. Ten udává limit znaků, které funkce v řetězci
zpracovávají. Pokud je řetězec delší, vrátí jen jeho část, useknutou
na maximální počet znaků. Pozor, tato část neobsahuje znak
\0
. Příkladem takové funkce budiž strncat()
.
V příští lekci, Textové řetězce v jazyce C podruhé - Práce se znaky, budeme s textovými řetězci v jazyce C pokračovat, ukážeme si jak pracovat s jednotlivými znaky.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkamiStaženo 960x (31.89 kB)