8. díl - Textové řetězce v jazyce C

C++ Základní konstrukce C Textové řetězce v jazyce C

Až doposud jsme se v našem seriálu ú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:

char text[5] = {'d', 'u', 'h', 'a', '\0'};
printf("%s", text);

Výsledek:

Definování textového řetězce v jazyce C

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:

char text[5] = "duha";
printf("%s", text);

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:

char text[] = "duha";
printf("%s", text);

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 ho změnit, případně řetězec zkrátit:

char text[] = "duha";
text[0] = 'h';
text[3] = '\0';
printf("%s", text);

Výsledek:

Změna textového řetězce v jazyce C

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í:

printf("Zadej své jméno: ");
char jmeno[51];
scanf("%50s", jmeno);
printf("Ahoj uživateli %s, vítám tě!", jmeno);

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):

scanf(" %50[^\n]s", jmeno);

Někdy můžete při načítání textu z konzole narazit na použití funkcí gets() nebo fgets(). 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 řětě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 výchá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.

strlen("duha"); // vrátí 4

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.

char text[20] = "duha";
strcat(text, " je na nebi"); // uloží do text "duha je na nebi"

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é.

char text[5];
strcpy(text, "duha");
printf("%s", text);

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.

char text[] = "Pan X znovu udeřil.";
int pozice = strchr(text, 'X') - text; // Zjistíme pozici znaku 'X' v textu
if (pozice >= 0)
{
        printf("Nalezeno na pozici %d", pozice);
}
else
{
        printf("Nenalezeno");
}

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 1 pokud je první za druhým.

strcmp("akát", "blýskavice"); // vrátí záporné číslo

Funkce pro práci s řětě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().


 

Stáhnout

Staženo 107x (31.89 kB)

 

  Aktivity (2)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (5 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (3)

Avatar
Milan Křepelka
Redaktor
Avatar
Milan Křepelka:

%c je právě jeden znak. %s je pole znaků, tedy string - text
http://www.codingunit.com/…atted-output

 
Odpovědět 18.10.2015 0:23
Avatar
Taskkill
Redaktor
Avatar
Taskkill:

Jak pise Milan .. char je od slova character, tedy znak... C nepracuje standardne se Stringem... nema ho tam... takze retezce uklada jako pole characteru... nicmene na vystup uz to musis specifikovat, to je jasne proc... vypisovac potrebuje vedet co do nej posilas...sam si na to neprijde, kdyz jde o data, ktera muzou bejt interpretovana ruzne viz char jako cislo char jako znak..

a ta druha otazka... scanf bere minimalne dva parametry... typ a ukazatel... ono totiz v cecku neni standardne mozny na ty nizsi vrstve nacist hodnotu a hodit ti ji jako return do vysi vrstvy ..neptej se me proc... nicmene se to resi trikem... druhy parametr neni promenna jmeno ale jen odkaz, ukazatel na ni..ten se znaci ampersandem tedy znakem & pred nazvem promenne...

snad vsechno jasne... za absenci diakritiky se omlouvam - en klavesnice ;)

 
Odpovědět  +1 18.10.2015 2:16
Avatar
ra3sk
Člen
Avatar
Odpovídá na Taskkill
ra3sk:

Viac menej áno. Keďže je stredoškolské vysvetlenie docela chabé tak to takto dohladávam po nete. Ďakujem za vysvetlenie. Je vysokopravdepo­dobné, že sa budem v tomto seriály ešte dosť pýtať. :-)

 
Odpovědět 18.10.2015 9:18
Avatar
ra3sk
Člen
Avatar
ra3sk:

Teraz som si to skúšal STRCOPY v Xcode a píše mi, že to nie je platné v C99. Je tu nejaká náhrada alebo?

 
Odpovědět 7.11.2015 20:25
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na ra3sk
David Novák:

STRCOPY? Pokud vím, tak v C nic takového není - pouze strcpy() v knihovně string.h

Odpovědět 7.11.2015 21:54
Chyba je mezi klávesnicí a židlí.
Avatar
Odpovídá na ra3sk
Libor Šimo (libcosenior):

Zda sa mi, ze som ti uz raz poslal link na lekciu o retazcoch od Herouta, alebo nie?

Odpovědět 8.11.2015 0:51
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na Libor Šimo (libcosenior)
Lukáš Horňák:

nazdar chlapi programátoři jak jde programování já jsem úplně blbý neumím zjistit ani délku řetězce tak mně prosím poradte, vy ste asi dobří když tu píšete takové moudra tak na vás spoléhám s pozdravem phpmaestro

 
Odpovědět 27. ledna 13:11
Avatar
Odpovídá na Lukáš Horňák
Libor Šimo (libcosenior):
#include <stdio.h>
#include <string.h> // použitie strlen()

int main(void)
{
    int dlzka = 0;
    char *retazec = "Nazdar chlapi programatori jak jde programovani?";

    printf("Dlzka retazca je %d.\n", strlen(retazec)); // pouzitie strlen()

    while(*retazec++ != '\0') { // dalsi jeden z moznych sposobov
        dlzka++;
    }
    printf("Dlzka retazca je %d.\n", dlzka);

    return 0;
}
Odpovědět 27. ledna 14:06
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na Lukáš Horňák
Libor Šimo (libcosenior):

Dôležité je uvedomiť si, že reťazec je jednorozmerné pole znakov, ukončené znakom '\0'.
Veľkosť poľa by si už mohol vedieľ zistiť aj sám.

Odpovědět  +1 27. ledna 14:08
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 13. Zobrazit vše