Informatika2-2012/Eloadas07
Ador (vitalap | szerkesztései) |
Ador (vitalap | szerkesztései) |
||
73. sor: | 73. sor: | ||
=== Fájl olvasás és írás === | === Fájl olvasás és írás === | ||
− | |||
− | + | Egy komoly adatfeldolgozást vagy bármilyen számításokat végző program praktikus ha fájlokból olvassa be az adatokat, és az eredményeket is fájlba írja. | |
− | + | Sajnos a file-kezelés C-ben nem túl egyszerű és nem "programozóbarát", de néhány működő példából kiindulva nem is olyan nehéz megoldani. | |
− | + | ||
− | + | A fájlkezeléshez szükséges függvényeket az "stdio.h" függvénykönvtár include-olásával tudjuk használni (ez kellett már korábban ahhoz is hogy a felhasználótól bekérjünk adatokat (vagyis a standard inputról olvassunk), vagy hogy a képrenyőre (standard output) írjunk). | |
− | + | ||
+ | Két sorban így néz ki egy fájl megnyitása (az "r" itt azt jeneti hogy olvasásra nyitjuk meg a szöveges fájlt): | ||
+ | <c> | ||
+ | FILE *fp; | ||
+ | fp = fopen("alma.txt", "r"); | ||
+ | </c> | ||
+ | |||
+ | Az "r" helyett lehetne "b" (bináris file olvasása), "w" (írás), "a" (a fájl végére írás (append)). | ||
+ | |||
+ | A "FILE * fp" egy olyan mutató ami "FILE" típusnevű struktúrára mutat. Ez a struktúra olyan információkat tartalmaz amik segítségével megvalósulhat a kapcsolat a programunk és az operációs rendszer (ami kezeli a fájlrendszert) között. A belsejét nem kell ismerni, elég azt tudni hogy hogy használjuk. | ||
+ | |||
+ | ===== Fájl olvasás fscanf()-fel ===== | ||
+ | |||
+ | A ''scanf()'' függvényt arra használtuk hogy a felhasználótól (a standard bemenetről) olvassunk vele egy-egy szót. | ||
+ | Ehhez hasonló a ''fscanf()'', ami fájlból (''input'') tud olvasni, amit olvasott azt az ''output'' változó(k)ba írja, itt egy karaktertömbbe(stringbe): | ||
+ | <c> | ||
+ | fscanf(input, "%s", output); | ||
+ | </c> | ||
+ | Azonban vigyázni kell vele, mert így csak az első szóközig (egy szót) olvas be! általános szöveges fájl beolvasására kevéssé alkalmas, inkább akkor hasznos, ha ismert szerkezetű fájlt kell beolvasni. | ||
+ | Például egy 3 dimenziós pont-koordinátákat tartalmazó fájlt beolvashatunk így, ha tudjuk hogy minden sorban egy pont adatai vannak, a sorokon belül pedig pontosan egy szóközzel vannak elválasztva a számok: | ||
+ | <c> | ||
+ | float x, y, z; | ||
+ | FILE *fp; | ||
+ | fp = fopen("koordinatak.txt", "r"); | ||
+ | fscanf(fp, "%f %f %f", &x, &y, &z); | ||
+ | </c> | ||
+ | A fenti példában az fscanf() csak az első sort olvassa be a fájlból, még egy ciklus kell köré hogy az egész fájlt feldolgozhassuk. | ||
+ | |||
+ | Példa: fileread1.c | ||
+ | |||
+ | ===== Fájl olvasás fread()-del ===== | ||
+ | |||
+ | Az ''fread()'' függvénnyel általánosabb file-beolvasót is írhatunk. Az ''fread()'' adott méretű blokkokat olvas be egy fájlból, elég nagy méretű blokk esetén akár az egészet. Visszatérési értéke hogy hány egységet (ált. karaktert) sikerült beolvasnia. | ||
+ | <c> | ||
+ | size_t fread(void *ptr, // ide olvassa be a fájl tartalmát | ||
+ | size_t size, // ekkora méretű elemekbe (tipikusan sizeof(char) lesz itt) | ||
+ | size_t nmemb, // ennyi darab (ennyi karakter) beolvasása maximum | ||
+ | FILE *stream); // ebből a fájlból | ||
+ | </c> | ||
+ | |||
+ | A beolvasott adatoknak a heap-en kell dinamikusan memóriát foglalnunk, ehhez pedig meg kell tudnuk a fájl méretét: | ||
+ | <c> | ||
+ | // fp egy megnyitott file mutatója | ||
+ | fseek(fp, 0L, SEEK_END); // a file végére ugrunk az "olvasófejjel" | ||
+ | long lFileLen = ftell(fp); // a file hosszát elkérjük az ftell()-lel | ||
+ | rewind(fp); // visszatekerünk a file elejére az "olvasófejjel" | ||
+ | </c> | ||
+ | |||
+ | A memóriába beolvasás után úgy dolgozzuk fel a fájl tartalmát (egy nagy karaktertömböt), | ||
+ | ahogy akarjuk. | ||
+ | |||
+ | (Ha nagyon nagy fájllal dolgozunk ami nem fér a memóriába, akkor kisebb blokkokat olvassunk be egy ciklusban, és blokonként dolgozzuk fel a tartalmat.) | ||
+ | |||
+ | Példa: fileread2.c | ||
+ | |||
+ | (Megjegyzés: van még néhány módja a file olvasásnak (karakterenként is olvashatjuk a fájlt például), de általában a fenti két módszer egyikével elég jól megoldható bármilyen fájl-olvasási feladat.) | ||
+ | |||
+ | |||
+ | ===== Fájl írás ===== | ||
+ | |||
+ | Nézzünk egy teljes példát fájl írására is. | ||
+ | A következő kis példaprogram 0-tól kezdve számokat ír ki egy fájlba, a fájl nevét és a számok számát a felhasználótól kéri be: | ||
<c> | <c> | ||
− | |||
− | |||
#include <stdio.h> | #include <stdio.h> | ||
− | main() { | + | |
+ | int main() { | ||
int i, N; | int i, N; | ||
− | FILE *fp; | + | FILE *fp; // fájl mutató |
− | char | + | char fname[80]; // a fájl neve lesz itt |
− | printf(" | + | printf("Mennyi számot generáljak?\n"); |
scanf("%d", &N); | scanf("%d", &N); | ||
− | printf(" | + | printf("A fájl neve ahova írjam:\n"); |
scanf("%s", fname); | scanf("%s", fname); | ||
− | fp = fopen(fname, "w"); | + | fp = fopen(fname, "w"); // itt nyitjuk meg a fájlt, írásra |
− | for (i = 0; i < N; i++) | + | if (fp == NULL) { // hibakezelés |
− | fprintf(fp, "% | + | printf("Nem sikerült megnyitni a fájlt: %s", fname); |
− | fclose(fp); | + | return 1; |
+ | } | ||
+ | for (i = 0; i < N; i++) { | ||
+ | fprintf(fp, "%d\n", i); // az "fprintf()" fájlba ír | ||
+ | } | ||
+ | fclose(fp); // be is zárjuk a fájlt ! | ||
} | } | ||
</c> | </c> | ||
− | === A | + | |
+ | === A programunk paraméterezése === | ||
+ | |||
main() paraméterezése | main() paraméterezése | ||
− | |||
<c> | <c> | ||
112. sor: | 177. sor: | ||
while(argc--) | while(argc--) | ||
printf("%s\n", *argv++); | printf("%s\n", *argv++); | ||
− | exit(EXIT_SUCCESS); | + | exit(EXIT_SUCCESS); // "return 0" helyett... az stdlib-ben van az exit() fv |
} | } | ||
</c> | </c> | ||
155. sor: | 220. sor: | ||
röviden a linkelésről. | röviden a linkelésről. | ||
+ | http://www.acm.uiuc.edu/webmonkeys/book/c_guide/1.7.html: | ||
+ | 1.7.3 #include | ||
+ | |||
+ | The #include directive allows external header files to be processed by the compiler. | ||
+ | |||
+ | Syntax: | ||
+ | |||
+ | #include <header-file> | ||
+ | |||
+ | or | ||
+ | |||
+ | #include "source-file" | ||
+ | |||
+ | When enclosing the file with < and >, then the implementation searches the known header directories for the file (which is implementation-defined) and processes it. When enclosed with double quotation marks, then the entire contents of the source-file is replaced at this point. The searching manner for the file is implementation-specific. | ||
+ | |||
+ | Examples: | ||
+ | |||
+ | #include <stdio.h> | ||
+ | #include "my_header.h" | ||
Gyakorlaton include-oljunk egy másik fáljt, ha lesz rá idő. | Gyakorlaton include-oljunk egy másik fáljt, ha lesz rá idő. |
A lap 2012. március 20., 16:24-kori változata
Tartalomjegyzék |
Egyéb hasznosságok C programozáshoz
Konstansok és makrók
A konstansok olyan "változók" amiknek az értékét nem lehet megváltoztani. Például hasznos lehet ilyen, ha a programban a pi értékét sok helyen szeretnénk használni (ehhez elég lenne egy globális változó..), és még véletlenül sem szeretnénk felülírni az értékét a program futása során, hogy minden számítás ugyanazzal a pontossággal számolódjon. Többféle megoldást is használhatunk erre.
Előfordítóval
Konstansokat az eredeti C-ben csak az előfordító segítségével hozhattunk létre. Minden olyan kódsor ami '#' jellej kezdődik, a preprocesszornak vagyis az előfeldolgozónak szóló utasítás. (Kicsit lejjebb lesznek még példák.)
A "#define" preprocesszor-utasítással definiálhatunk konstansokat. Csak abban a file-ban érvényes egy konstans ahol deklaráltuk.
/* Konstansdeklaráció: eredeti C-s megoldás, előfordítónak szóló utasítással */ #define PI 3.14159 #define NULLA 0 #define SZOVEG "Ha ezt a sort el tudja olvasni, nincs szüksége szemüvegre \n"
Itt nem kell pontosvessző a sorok végére. Az előfordító a program lefordítása előtt a konstansok minden előfordulási helyére behelyettesíti a megfelelő értéket (betűről betűre, kiértékelés nélkül, lecseréli pl. a "PI"-t "3.14"-re).
A konstansneveket mindig nagybetűvel kell írni!
C-ben egy konstansot megsemmisíteni is lehet az "#undef konstansnév" paranccsal, illetve ellenőrizhetjük az "#ifdef" ill. "#ifndef" preprocesszor-parancsokkal, hogy van-e (ill. nincs-e) már definiálva adott névvel konstans (ne felejtsük el lezárni az if-et):
#ifndef PI #define PI 3.14 #endif
Makro-függvények
Akár kicsi makrofüggvények írásásra is használhatjuk ezt a behelyeetesítő mechanizmust. De vigyáznunk kell arra hogy az előfordító, mint nyelven kívüli eszköz mindent gondolkodás nélkül helyettesít, ráadásul az eredményt egy sorba írva azt sem teszi lehetővé, hogy a makrohelyettesítést lépésenként nyomkövessük.
Egy elrettentő példa:
// a háromoperandusú feltételes operátort használjuk, mert rövid #define abs(val) (val < 0) ? -val : val int y, x = 3; y = abs( x++ ); // Várt eredmény: x = 4, y = 3;
Az abszolút érték makro fenti alkalmazása esetén az előfordító a hívás helyén (ahol y-nak adunk értéket) behelyettesíti az abs() makrot, a belsejébe pedig paraméterként behelyettesíti a "val" helyére az "x++"-t. Első ránézésre azt várnánk, hogy az y = abs(x++) végrehajtása után, mivel előtte x értéke 3 volt, x értéke 4 lesz, míg y értéke 3. Ez így is lenne, ha az abs-ot függvényként realizálnánk. Ezzel szemben a előfordító ebből a sorból a következőt készíti:
y = (x++ < 0) ? - x++ : x++;
azaz az x-et kétszer növeli, minek következtében az utasítás végrehajtása után x értéke 5, míg y-é 4 lesz. A előfordítóval definiált makrok tehát veszélyesek lehetnek.
A const típusmódosítóval
Az ANSI (szabványos) C-ben (és majd C++-ban) a const típusmódosító szó segítségével bármely memóriaobjektumot definiálhatunk konstansként, vagyis "csak olvasható változó"-ként. Ez azt jelenti, hogy a fordító figyelmeztet, ha a változó nevét értékadás bal oldalán szerepeltetjük, vagy ebből nem konstansra mutató pointert inicializálunk.
/* ANSI C megoldás PI definiálására */ const float PI = 3.14;
Mutatók esetén lehetőség van annak megkülönböztetésére, hogy a mutató által megcímzett objektumot, vagy magát a mutatót szeretnénk csak olvashatóvá tenni:
const char * p; // p által címzett karakter nem módosítható, (const char) * p char * const q; // q mutatót nem lehet megváltoztatni, (char *) const q
Fájl olvasás és írás
Egy komoly adatfeldolgozást vagy bármilyen számításokat végző program praktikus ha fájlokból olvassa be az adatokat, és az eredményeket is fájlba írja.
Sajnos a file-kezelés C-ben nem túl egyszerű és nem "programozóbarát", de néhány működő példából kiindulva nem is olyan nehéz megoldani.
A fájlkezeléshez szükséges függvényeket az "stdio.h" függvénykönvtár include-olásával tudjuk használni (ez kellett már korábban ahhoz is hogy a felhasználótól bekérjünk adatokat (vagyis a standard inputról olvassunk), vagy hogy a képrenyőre (standard output) írjunk).
Két sorban így néz ki egy fájl megnyitása (az "r" itt azt jeneti hogy olvasásra nyitjuk meg a szöveges fájlt):
FILE *fp; fp = fopen("alma.txt", "r");
Az "r" helyett lehetne "b" (bináris file olvasása), "w" (írás), "a" (a fájl végére írás (append)).
A "FILE * fp" egy olyan mutató ami "FILE" típusnevű struktúrára mutat. Ez a struktúra olyan információkat tartalmaz amik segítségével megvalósulhat a kapcsolat a programunk és az operációs rendszer (ami kezeli a fájlrendszert) között. A belsejét nem kell ismerni, elég azt tudni hogy hogy használjuk.
Fájl olvasás fscanf()-fel
A scanf() függvényt arra használtuk hogy a felhasználótól (a standard bemenetről) olvassunk vele egy-egy szót. Ehhez hasonló a fscanf(), ami fájlból (input) tud olvasni, amit olvasott azt az output változó(k)ba írja, itt egy karaktertömbbe(stringbe):
fscanf(input, "%s", output);
Azonban vigyázni kell vele, mert így csak az első szóközig (egy szót) olvas be! általános szöveges fájl beolvasására kevéssé alkalmas, inkább akkor hasznos, ha ismert szerkezetű fájlt kell beolvasni. Például egy 3 dimenziós pont-koordinátákat tartalmazó fájlt beolvashatunk így, ha tudjuk hogy minden sorban egy pont adatai vannak, a sorokon belül pedig pontosan egy szóközzel vannak elválasztva a számok:
float x, y, z; FILE *fp; fp = fopen("koordinatak.txt", "r"); fscanf(fp, "%f %f %f", &x, &y, &z);
A fenti példában az fscanf() csak az első sort olvassa be a fájlból, még egy ciklus kell köré hogy az egész fájlt feldolgozhassuk.
Példa: fileread1.c
Fájl olvasás fread()-del
Az fread() függvénnyel általánosabb file-beolvasót is írhatunk. Az fread() adott méretű blokkokat olvas be egy fájlból, elég nagy méretű blokk esetén akár az egészet. Visszatérési értéke hogy hány egységet (ált. karaktert) sikerült beolvasnia.
size_t fread(void *ptr, // ide olvassa be a fájl tartalmát size_t size, // ekkora méretű elemekbe (tipikusan sizeof(char) lesz itt) size_t nmemb, // ennyi darab (ennyi karakter) beolvasása maximum FILE *stream); // ebből a fájlból
A beolvasott adatoknak a heap-en kell dinamikusan memóriát foglalnunk, ehhez pedig meg kell tudnuk a fájl méretét:
// fp egy megnyitott file mutatója fseek(fp, 0L, SEEK_END); // a file végére ugrunk az "olvasófejjel" long lFileLen = ftell(fp); // a file hosszát elkérjük az ftell()-lel rewind(fp); // visszatekerünk a file elejére az "olvasófejjel"
A memóriába beolvasás után úgy dolgozzuk fel a fájl tartalmát (egy nagy karaktertömböt), ahogy akarjuk.
(Ha nagyon nagy fájllal dolgozunk ami nem fér a memóriába, akkor kisebb blokkokat olvassunk be egy ciklusban, és blokonként dolgozzuk fel a tartalmat.)
Példa: fileread2.c
(Megjegyzés: van még néhány módja a file olvasásnak (karakterenként is olvashatjuk a fájlt például), de általában a fenti két módszer egyikével elég jól megoldható bármilyen fájl-olvasási feladat.)
Fájl írás
Nézzünk egy teljes példát fájl írására is. A következő kis példaprogram 0-tól kezdve számokat ír ki egy fájlba, a fájl nevét és a számok számát a felhasználótól kéri be:
#include <stdio.h> int main() { int i, N; FILE *fp; // fájl mutató char fname[80]; // a fájl neve lesz itt printf("Mennyi számot generáljak?\n"); scanf("%d", &N); printf("A fájl neve ahova írjam:\n"); scanf("%s", fname); fp = fopen(fname, "w"); // itt nyitjuk meg a fájlt, írásra if (fp == NULL) { // hibakezelés printf("Nem sikerült megnyitni a fájlt: %s", fname); return 1; } for (i = 0; i < N; i++) { fprintf(fp, "%d\n", i); // az "fprintf()" fájlba ír } fclose(fp); // be is zárjuk a fájlt ! }
A programunk paraméterezése
main() paraméterezése
#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { while(argc--) printf("%s\n", *argv++); exit(EXIT_SUCCESS); // "return 0" helyett... az stdlib-ben van az exit() fv }
Example 10.1
http://www.cprogramming.com/tutorial/c/lesson14.html
- include <stdio.h>
int main ( int argc, char *argv[] ) { /* argc should be 2 for correct execution */ if (argc != 2) { /* We print argv[0] assuming it is the program name */ printf( "usage: %s filename", argv[0] ); } else { // We assume argv[1] is a filename to open FILE *file = fopen( argv[1], "r" ); /* fopen returns 0, the NULL pointer, on failure */ if (file == 0) { printf( "Could not open file\n" ); } else { int x; /* read one character at a time from file, stopping at EOF, which indicates the end of the file. Note that the idiom of "assign to a variable, check the value" used below works because the assignment statement evaluates to the value assigned. */ while ((x = fgetc(file)) != EOF ) { printf( "%c", x ); } fclose( file ); } } }
Több fájlból álló program
röviden a linkelésről.
http://www.acm.uiuc.edu/webmonkeys/book/c_guide/1.7.html: 1.7.3 #include
The #include directive allows external header files to be processed by the compiler.
Syntax:
#include <header-file>
or
#include "source-file"
When enclosing the file with < and >, then the implementation searches the known header directories for the file (which is implementation-defined) and processes it. When enclosed with double quotation marks, then the entire contents of the source-file is replaced at this point. The searching manner for the file is implementation-specific.
Examples:
#include <stdio.h> #include "my_header.h"
Gyakorlaton include-oljunk egy másik fáljt, ha lesz rá idő.
C függvénykönyvtárak
Matematikai függvények
math.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.7.html
Standard definíciók
stddef.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.11.html
Standard I/O
stdio.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.12.html
Standard dolgok
stdlib.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.13.html
Macros:
NULL RAND_MAX
Variables:
typedef size_t typedef wchar_t
Functions: abs(); labs(); atof(); atoi(); atol(); malloc(); calloc(); free(); qsort(); rand(); srand();
Stringből számmá konvertáló függvények
atoi(), atof(),
Random számok generálása
rand(); srand();
Stringek
string.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.14.html
Ellenőrző kérdések
Amit a zh-ra tudni kell:
- makrók definiálása
- stringek kezelése (előző? előadás végén is volt)
- main paraméterezése hogy néz ki
- file olvasás, írás
Források és további olvasnivalók:
- http://www.tankonyvtar.hu/informatika/objektum-orientalt-080905-103
- http://www.math.utah.edu/~carlson/c/cbook.pdf
- The C library reference guide: http://www.acm.uiuc.edu/webmonkeys/book/c_guide/index.html
- http://www.cprogramming.com/tutorial/c/lesson14.html
- http://publications.gbdirect.co.uk/c_book/chapter10/arguments_to_main.html
- http://en.wikipedia.org/wiki/Include_guard