Τμήμα Ηλεκτρονικών Μηχανικών Τ.Ε.Ι. Κρήτης Προγραμματισμός Η/Υ (ΤΛ2007 ) Δρ. Μηχ. Νικόλαος Πετράκης (npet@chania.teicrete.gr) Δέκατη-τρίτη (13 η ) τρίωρη διάλεξη. Ιστοσελίδα Μαθήματος: https://eclass.chania.teicrete.gr/ Εξάμηνο: Εαρινό 2016-17
Προσπέλαση Αρχείων Μέχρι τώρα όποτε μιλούσαμε για αρχείο εισόδου θεωρούσαμε την πρότυπη (στάνταρτ) είσοδο και όποτε μιλούσαμε για αρχείο εξόδου θεωρούσαμε την πρότυπη έξοδο, που σε κάθε πρόγραμμα C ορίζονται αυτόματα από το τοπικό λειτουργικό σύστημα. Στην συνέχεια θα δούμε πως μπορούμε να προσπελάσουμε αρχεία που δεν είναι ήδη συνδεδεμένα με το πρόγραμμα. Π.χ. cat prog1.c prog2.c // συνενώνει ένα σύνολο κατονομαζόμενων // αρχείων στην οθόνη Το πρόβλημα είναι πως να οργανώσουμε το διάβασμα ή την εγγραφή των αναφερόμενων αρχείων - δηλαδή, πως να συνδέσουμε τα εξωτερικά ονόματα που έχει στο νου του ο χρήστης, με τις εντολές που διαβάζουν τα δεδομένα. 2
Κανόνες Βήματα για την Προσπέλαση Αρχείων Για να μπορεί να γραφτεί ή να διαβαστεί ένα αρχείο, πρέπει να ανοιχτεί με την συνάρτηση βιβλιοθήκης fopen(), η οποία παίρνει ένα εξωτερικό όνομα (όπως το prog1.c ή prog2.c) και επιστρέφει ένα δείκτη διεύθυνσης (pointer) για να χρησιμοποιηθεί στις επόμενες αναγνώσεις ή καταγραφές του αρχείου. Ο δείκτης αυτός, που λέγεται δείκτης διεύθυνσης αρχείου (file pointer), δείχνει μια δομή που περιέχει πληροφορίες για το αρχείο, όπως η θέση μιας περιοχής ενδιάμεσης αποθήκευσης (buffer), η τρέχουσα θέση χαρακτήρα στην περιοχή ενδιάμεσης αποθήκευσης, το αν γράφεται ή διαβάζεται το αρχείο, κι αν έχουν συμβεί λάθη ή αν έχει συναντηθεί το τέλος του αρχείου. Η μόνη δήλωση που χρειάζεται να γίνει για δείκτη αρχείου είναι: FILE *fp; 3
Βήματα για την Προσπέλαση Αρχείων Το πρωτότυπο της fopen() είναι: FILE *fopen(char *name, char *mode); Η κλήση της fopen σε ένα πρόγραμμα είναι: fp = fopen (name, mode); // r ανάγνωση, w εγγραφή, a προσθήκη, b δυαδικό Προσοχή: Ο προεπιλεγμένος τύπος αρχείων είναι τα αρχεία κειμένου. π.χ. η εντολή fopen( test.dat, wb ); ανοίγει για εγγραφή ένα δυαδικό αρχείο, ενώ η εντολή fopen( test1.txt, w ); ανοίγει για εγγραφή ένα αρχείο κειμένου. Το επόμενο που χρειάζεται είναι ένας τρόπος για την ανάγνωση ή την καταγραφή στο αρχείο όταν αυτό είναι ανοιχτό. Υπάρχουν διάφορες δυνατότητες, από τις απλούστερες είναι οι: getc // int getc(file *fp); putc // int putc(int c, FILE *fp); // EOF για τέλος αρχείου ή λάθος Για φορμαρισμένη είσοδο και έξοδο αρχείων, μπορούν να χρησιμοποιηθούν οι συναρτήσεις: fscanf // int fscanf(file *fp, char *format ); fprintf // int fprintf(file *fp, char *format ); 4
Βήματα για την Προσπέλαση Αρχείων Η αντίστροφη της fopen() είναι η συνάρτηση : int fclose(file *fp); η οποία διακόπτει τη σύνδεση ανάμεσα στο δείκτη αρχείου και το εξωτερικό όνομα που έχει καθοριστεί με την fopen(), ελευθερώνοντας το δείκτη αρχείου. Τα λειτουργικά έχουν όριο στον αριθμό των ταυτόχρονα ανοικτών αρχείων. Σε αρχεία εξόδου αδειάζει την περιοχή ενδιάμεσης αποθήκευσης στην οποία η putc() συλλέγει την έξοδο. Κατά τον κανονικό τερματισμό του προγράμματος, η fclose() καλείται, αυτόματα για όλα τα ανοικτά αρχεία. 5
Ανίχνευση τέλους αρχείου Όταν δεν γνωρίζουμε πόσα στοιχεία περιέχει ένα αρχείο και θέλουμε να διαβάσουμε τα δεδομένα από την αρχή μέχρι το τέλος του, θα πρέπει να ανιχνεύσουμε το τέλος του αρχείου. Όταν διαβάζουμε από ένα αρχείο κειμένου χαρακτήρα προς χαρακτήρα, μπορούμε να ψάξουμε το χαρακτήρα τέλους αρχείου. Η συμβολική σταθερά ΕΟF ως -1 (ή ένα byte όλο άσσους) έχει τιμή που δε χρησιμοποιείται ποτέ από ένα πραγματικό χαρακτήρα. Όταν μία συνάρτηση διαβάσει το ΕΟF από μία ροή κειμένου, τότε ξέρουμε ότι έχουμε φτάσει στο τέλος του αρχείου. while ((c = getc(fp))!= ΕΟF ) Σε μία δυαδική ροή, ένα byte δεδομένων θα μπορούσε να έχει αυτή τη τιμή, γι αυτό μπορούμε να χρησιμοποιήσουμε τη συνάρτηση feof() για δυαδικά αρχεία αλλά και για αρχεία κειμένου. int feof(file *fp); Το όρισμα fp είναι ο δείκτης σε FILE που επιστρέφεται από τη fopen() H feof() επιστρέφει 0 αν δεν συναντήσει το τέλος του αρχείου και μη μηδενική τιμή αν το συναντήσει. Εάν μία κλήση της feof() ανιχνεύσει το τέλος του αρχείου τότε πρέπει ή να κάνουμε rewind(), fseek() ή να κλείσουμε και να ανοίξουμε το αρχείο ξανά. 6
Άλλες συναρτήσεις διαχείρισης αρχείου int remove(const char *filename); Εάν το αρχείο υπάρχει, διαγράφεται και επιστρέφει 0. Αν το αρχείο δεν υπάρχει ή είναι μόνο για ανάγνωση, επιστρέφει -1. int rename(const char *oldname, const char *newname); Επιστρέφει 0 σε επιτυχία. Αν το αρχείο δεν υπάρχει ή υπάρχει ήδη ένα αρχείο με το νέο όνομα ή επιχειρούμε να μετονομάσουμε σε διαφορετικό δίσκο τότε επιστρέφει -1. 7
#include <stdio.h> main(){ FILE *ifp, *ofp; char c; ifp = fopen( test.dat, r ); ofp = fopen( out.dat, w ); Παράδειγμα Να γίνει πρόγραμμα το οποίο θα διαβάζει χαρακτήρες από το αρχείο ifp ( test.dat ) με getc() και θα τους αντιγράφει στο αρχείο ofp ( out.dat ) με putc(). if (ifp == NULL) { printf( Can not open input file\n ); return 1; if (ofp == NULL) { printf( Can not open output file\n ); return 2; while((c = getc(ifp))!= EOF) putc(c, ofp); fclose(ifp); fclose(ofp); return 0; Προσοχή: Πάντα όταν προσπαθούμε να ανοίξουμε ένα αρχείο πρέπει να ελέγχουμε αν το άνοιγμα έγινε επιτυχώς. Σε αντίθετη περίπτωση (π.χ. αν δεν υπάρχει αρχείο με το όνομα που δώσαμε), η fopen επιστρέφει NULL. 8
Άσκηση: Να γίνει ένα πρόγραμμα που να αντιγράφει ένα αρχείο με κώδικα γραμμένο σε γλώσσα C, σε ένα άλλο αφαιρώντας όλα τα σχόλια (/* */). Θεωρήστε ότι τα σχόλια δεν είναι επάλληλα, δηλαδή φωλιασμένα το ένα μέσα στο άλλο. Συγκρίνετέ το με το πρόγραμμα που κάνει την ίδια δουλειά αντιγράφοντας, όμως, από την στάνταρτ είσοδο (stdin) στην στάνταρτ έξοδο (stdout) 9
#include <stdio.h> // program nocomment #define NO 0 // stdin stdout #define YES 1 #define NC -3 main() { int car, cp=nc, comment=no; printf("\n Δώστε κείμενο (^z για τέλος) :\n"); while ((car=getchar())!=eof) { if (comment==no) { if ((cp=='/')&&(car=='*')) { comment=yes; car=nc; else if (cp!=nc) putchar(cp); else if ((cp=='*')&&(car=='/')) { comment=no; car=nc; cp = car; if ((comment==no)&&(cp!=nc)) putchar(cp); return 0; 10
#include <stdio.h> // program nocomment #define NO 0 // test.c testnc.c #define YES 1 #define NC -3 main() { int car, cp=nc, comment=no; FILE *fps, *fpd; printf("\n ***Copy Files***\n"); if ((fps=fopen("test.c","r"))==null) { printf("\n File not found\n\n"); exit(1); if ((fpd=fopen("testnc.c","w"))==null) { printf("\n File not accessible\n\n"); exit (1); while ((car=getc(fps))!=eof) { if (comment==no) { if ((cp=='/')&&(car=='*')) { comment=yes; car=nc; else if (cp!=nc) putc(cp,fpd); else if ((cp=='*')&&(car=='/')) { comment=no; car=nc; cp = car; if ((comment==no)&&(cp!=nc)) putc(cp,fpd); fclose(fps); fclose(fpd); return 0; 11
Συνοπτικά Ανοίγετε ένα αρχείο πριν επιχειρήσετε να διαβάσετε ή να γράψετε σε αυτό. Πάντοτε να ελέγχετε ότι η πρόσβαση σε ένα αρχείο ήταν σωστή. Κλείνετε όλα τα αρχεία που έχετε ανοίξει και δεν χρειάζεστε πλέον. 12