LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> C/C++ (10) - Standardní vstup a výstup

Dnes se naučíme přečíst ze standardního vstupu řádek nebo i jednotlivé znaky. Ukážeme si také, jak nebezpečnou funkcí gets snadno a rychle ukončit program.

23.12.2004 15:00 | Jan Němec | Články autora | přečteno 66492×

Standardní vstup a výstup

Pilný čtenář prvních devíti dílů tohoto seriálu už umí v Céčku leccos napsat. Pomocí podmínek, cyklů a funkcí zakóduje i poměrně složitý algoritmus a funkcemi printf a puts vypíše výsledek. Jeho program ale bude pokaždé počítat přesně to samé, chybí nám zatím obousměrná komunikace s uživatelem. Nejjednodušší (a všem příznivcům Linuxu blízký) je standardní terminálový vstup a výstup.

Řetězce jako pole znaků

Už víme, jak se v C píší řetězcové konstanty, umíme také používat řetězce jako ukazatele na char. Problém nastane, když zkusíme řetězce měnit.

#include <stdio.h>

int main(void) {
  char *s = "krása";

  s[2] = 'y';
  puts(s);
  return 0;
}

Tento program nevypíše ani krása ani krysa, ale Segmentation fault. Při definici řetězce překladač vyhradí (alespoň) 6 bytů v paměti, kam nemáme právo zápisu, a obsahem těch šesti bytů bude 'k', 'r', 'á', 's', 'a' a 0. Při pokusu o přepsání znaku 'á' program spadne.

Úspěšnější budeme, pokud řetězec definujeme jako pole znaků.

 /* Nezapomenout ukončovací 0! Neplést 0 a '0'! */
char s[6] = {'k', 'r', 'á', 's', 'a', 0};

V tom případě je řetězec v paměti s právem zápisu a můžeme jej modifikovat. V inicializaci je navíc možné i zde použít únosnější řetězcovou syntaxi

char s[6] = "krása";

Mimo inicializaci by ovšem přiřazení řetězce do řetězce - pole neprošlo.

char s[6] = "krása"; /* definice s inicializací, OK */
char   *p = "krása"; /* definice s inicializací, OK */

s = "krysa";  /* chyba při překladu, pole je konstantní ukazatel*/
p = "krysa";  /* změna hodnoty p, nikoliv obsahu paměti, kam původní p ukazovalo */
s[2] = 'y';   /* korektní změna paměti na adrese s + 2 */
p[2] = 'y';   /* pokus o změnu paměti na adrese s + 2, projde překladem,
                 Segmentation fault za běhu */

Modifikovat řetězce pomocí přístupu k jednotlivým znakům je poněkud nepohodlné, standardní knihovna C pochopitelně nabízí celou řadu funkcí, které umí například kopírovat nebo spojovat řetězce. Probereme je v některém z příštích dílů.

Znakově orientovaný vstup a výstup

Jeden znak ze standardního vstupu přečteme pomocí příkazu getchar(). Schválně neříkám funkce getchar, neboť ve skutečnosti nemusí být implementovaný jako funkce, ale pro běžné použití je to jedno. Příkaz getchar() nemá parametry a vrací int, který má hodnotu EOF, pokud došlo k chybě, nebo ASCII kód znaku ze standardního vstupu. Nejčastější chybou je konec souboru, k tomu dojde běžně při přesměrování standardního vstupu nebo pokud uživatel ukončí vstup (na Linuxu Ctrl + D).

puts("Chceš pozdravit? (A/N)");
if (getchar() == 'A')
  puts("Ahoj");

Je dobré vědět, že cesta znaku od klávesnice ke getcharu je dlouhá a vede přes nejrůznější buffery včetně toho ze standardní C knihovny. Do našeho programu se vstup dostane teprve s celou řádkou (ukončenou znakem '\n'), až uživatel zmáčkne enter. Pokud není žádný vstup k dispozici, getchar čeká na uživatele, v opačném případě vrátí znak z bufferu, i když třeba ke zmáčknutí příslušné klávesy došlo ve skutečnosti před voláním getchar.

Pokud tedy spustíme následující program

#include <stdio.h>

int main(void) {
  while (1) {
    int c = getchar();
    if (c == EOF) break;
    printf("Zadal jsi znak '%c' s ASCII kódem %i\n", c, c);
  }
  return 0;
}

uživatel chvíli počká a pak zadá 456[enter], první getchar bude čekat a po odentrování vrátí '4'. Další tři volání skončí okamžitě a vrátí postupně '5', '6' a '\n'. Následující getchar opět čeká na uživatelský vstup. Na obrazovce bude

456
Zadal jsi znak '4' s ASCII kódem 52
Zadal jsi znak '5' s ASCII kódem 53
Zadal jsi znak '6' s ASCII kódem 54
Zadal jsi znak '
' s ASCII kódem 10

Program skončí až s koncem standardního vstupu (Ctrl + D).

Kvůli speciální hodnotě EOF nevrací getchar znak jako char, ale jako int. Pokud chceme získaný znak využít v proměnné typu char, je třeba přetypovat.

char s[6] = "krása";
int c;

puts("Zadej znak 'y'");
c = getchar();
if (c == EOF)
  puts("chyba");
else
  s[2] = (char) c;

Vypisovat jeden znak pomocí funkce printf je poněkud neohrabané, proto je ve standardní knihovně příkaz duální ke getchar a jmenuje se putchar. Místo printf("%c", c); tak můžeme psát putchar(c);. Parametrem i návratovou hodnotou je int - ASCII kód vypisovaného znaku, v případě neočekávané chyby vrací putchar EOF.

Čtení a zápis řádky

Programátor většinou nepotřebuje číst a psát jednotlivé znaky, ale zpracovává vstup a výstup po řádkách. Zde nabízí standardní C knihovna funkce gets a puts. Funkci puts už známe, vypíše řetězec na standardní výstup a odřádkuje. Při návrhu duální funkce gets bohužel autoři příliš nemysleli.

char *gets(char *s);

Funkce čte standardní vstup, dokud nenarazí oddělovač řádku (znak '\n') nebo nedojde k chybě, výsledek ukládá do parametru s a (celkem zbytečně) je s také návratovou hodnotou. V případě chyby nebo okamžitého konce vstupu vrací funkce konstantu NULL. Po úspěšném načtení řádku přidá funkce na konec řetězce ukončovací nulu, kterou tak nahradí znak '\n'. Zásadním problémem gets je, že neumožňuje programátorovi stanovit maximální délku vstupu.

char s[256];
puts("Zadejte řetězec kratší 256 znaků, jinak nejspíš program spadne.");
gets(s);

Takhle se programy psát nedají. Je dobré si uvědomit, že uživatel může spustit program s přesměrovaným standardním vstupem ze souboru (s potenciálně dlouhými řádky), takže může dojít k nepříjemnostem i bez zlého úmyslu uživatele.

Naštěstí je ve standardní knihovně také funkce pro čtení řádky ze souboru, a ta už má maximální délku omezenou.

char *fgets(char *s, int size, FILE *stream);

Od gets se liší především dvěma parametry. První, size, je maximální počet přečtených znaků včetně ukončovací nuly. Pokud je řádek delší, funkce načte jenom size - 1 znaků a korektně ukončí řetězec nulou. Případné další volání fgets začne číst vstup tam, kde předchozí fgets skočila. Další parametr je soubor, ze kterého budeme číst. Pro standardní vstup lze použít proměnnou ze stdio.h jménem stdin. Další drobný rozdíl gets a fgets spočívá v chování na konci řádky. Zatímco gets znak '\n' nepřidá do načteného řetězce, fgets ano.

char s[256];
puts("Zadejte řetězec kratší 256 znaků, jinak ho oříznu.");
fgets(s, 256, stdin);

Příklad pro dnešní díl

Pomlouval jsem funkci gets, proto ukážu ještě jednu náhradu - vlastní implementaci.

#include <stdio.h>
/* Bezpečná gets */
char * moje_gets(char *s, int size) {
  int i, c;

  /* Kvůli nule na konci řetězce */
  size--; 
  /* Buffer velikosti <= 0 */
  if (size < 0) return NULL;
  for(i = 0; i < size; i++) {
    c = getchar();
    /* Konec vstupu nebo řádky */
    if (c == EOF || c == '\n') break; 
    s[i] = (char) c;
  }
  /* Ukončovací 0 */
  s[i] = 0; 
  /* Pokud došlo k chybě hned na začátku, vracím NULL. */
  if (!i && c == EOF) return NULL; 
  /* Jinak vracím s. */
  return s;
}

int main(void) {
  char s[10];
  
  moje_gets(s, 10);
  puts(s);
  return 0;
}

Pokračování příště

V dalším dílu povídání o vstupu dokončím. Dojde i na čtení čísel a konverzi řetězce na čísla a obráceně.

Verze pro tisk

pridej.cz

 

DISKUZE

jeden znak 28.12.2004 18:39 p f
  |- Re: jeden znak 29.12.2004 09:36 Jan Němec
  | L Re: jeden znak 1.1.2005 20:19 Aleš Hakl
  |   L Re: jeden znak 1.1.2005 20:34 Aleš Hakl
  L Re: jeden znak 4.10.2007 15:21 Jan Němec
    |- Re: jeden znak 4.10.2007 16:20 Aleš Hakl
    | L Re: jeden znak 4.10.2007 17:13 Jan Němec
    L Re: jeden znak 14.3.2014 22:09 xerostomus




Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

13.9.2017 8:00 /František Kučera

Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt – tentokrát netradičně v pondělí: 18. září od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).


Přidat komentář

3.9.2017 20:45 /Redakce Linuxsoft.cz
PR: Dne 21. září 2017 proběhne v Praze konference "Mobilní řešení pro business". Hlavní tématy konference budou: nejnovější trendy v oblasti mobilních řešení pro firmy, efektivní využití mobilních zařízení, bezpečnostní rizika a řešení pro jejich omezení, správa mobilních zařízení ve firmách a další.
Přidat komentář

15.5.2017 23:50 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt, který se bude konat ve čtvrtek 18. května od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

12.5.2017 16:42 /Honza Javorek
PyCon CZ, česká konference o programovacím jazyce Python, se po dvou úspěšných ročnících v Brně bude letos konat v Praze, a to 8. až 10. června. Na konferenci letos zavítá např. i Armin Ronacher, známý především jako autor frameworku Flask, šablon Jinja2/Twig, a dalších projektů. Těšit se můžete na přednášky o datové analytice, tvorbě webu, testování, tvorbě API, učení a mentorování programování, přednášky o rozvoji komunity, o použití Pythonu ve vědě nebo k ovládání nejrůznějších zařízení (MicroPython). Na vlastní prsty si můžete na workshopech vyzkoušet postavit Pythonem ovládaného robota, naučit se učit šestileté děti programovat, efektivně testovat nebo si v Pythonu pohrát s kartografickým materiálem. Kupujte lístky, dokud jsou.
Přidat komentář

2.5.2017 9:20 /Eva Rázgová
Putovní konference československé Drupal komunity "DrupalCamp Československo" se tentokrát koná 27. 5.2017 na VUT FIT v Brně. Můžete načerpat a vyměnit si zkušenosti z oblasti Drupalu 7 a 8, UX, SEO, managementu týmového vývoje, využití Dockeru pro Drupal a dalších. Vítáni jsou nováčci i experti. Akci pořádají Slovenská Drupal Asociácia a česká Asociace pro Drupal. Registrace na webu .
Přidat komentář

1.5.2017 20:31 /Pavel `Goldenfish' Kysilka
PR: 25.5.2017 proběhne v Praze konference na téma Firemní informační systémy. Hlavními tématy jsou: Informační systémy s vlastní inteligencí, efektivní práce s dokumenty, mobilní přístup k datům nebo využívání cloudu.
Přidat komentář

15.4.2017 15:20 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě IoT a radiokomunikace? Přijď na sraz spolku OpenAlt, který se bude konat ve středu 19. dubna od 18:30 v Šenkovně (Sokolská 60, Praha 2).
Přidat komentář

5.3.2017 19:12 /Redakce Linuxsoft.cz
PR: 23. března proběhne v Praze konferenci na téma Cloud computing v praxi. Hlavními tématy jsou: Nejžhavější trendy v oblasti cloudu a cloudových řešení, Moderní cloudové služby, Infrastruktura současných cloudů, Efektivní využití cloudu, Nástrahy cloudových řešení a jak se jim vyhnout.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

18.9.2017 14:37 / Rojas
high security vault

15.9.2017 7:33 / Wilson
new zealand childcare jobs

31.8.2017 12:11 / Jaromir Obr
Re: ukůládání dat ze souboru

30.7.2017 11:12 / Jaromir Obr
Národní znaky

27.7.2017 12:24 / Jaromir Obr
Cteni/zapis

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2017) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze