Είσοδος / Έξοδος και Σφάλματα <stdio.h>


  Αυτό το κεφάλαιο εξετάζει διάφορες μορφές Εισόδου/Εξόδου (Ε/Ε).Ήδη στα προγράμματα περιλαμβάνουμε συστηματικά το αρχείο κεφαλής 

   #include <stdio.h>

Καταγραφή Σφαλμάτων

Πολλές φορές είναι χρήσιμη η καταγραφή των σφαλμάτων σε ένα πρόγραμμα C. Η πρότυπη συνάρτηση βιβλιοθήκης perror() είναι εύχρηστη και βολική. Χρησιμοποιείται σε συνδυασμό με το errno . Αν και δεν είναι αυστηρά τμήμα της βιβλιοθήκης stdio.h εισάγουμε την χρήση του errno και της συνάρτησης exit()

perror()

Η συνάρτηση perror() δηλώνεται ως εξής:

void perror(const char *message);

Η perror() παράγει ένα μήνυμα (στο πρότυπο σφάλμα, stderr) που περιγράφει το τελευταίο σφάλμα που συνέβη κατά τη κλήση μιας συνάρτησης βιβλιοθήκης ή κλήσης συστήματος και το οποίο επέστρεψε έναν αριθμό σφάλματος  errno . Το όρισμα  message, εμφανίζεται πρώτο, μετά ένα ερωτηματικό και ένα κενό. Κατόπιν ακολουθεί το μήνυμα του συστήματος που αντιστοιχεί στο  errno . Αν το message είναι δείκτης NULL ή είναι κενό, το ερωτηματικό δεν εμφανίζεται, αλλά μόνο τοι μήνυμα του συστήματος.

Επίσης υπάρχουν και ορισμένες άλλες συναρτήσεις εμφάνισης σφαλμάτων, όπως η strerror() η οποία αποθηκεύει τα μηνύματα σε απομονωτή που επιλέγει ο προγραμματιστής. Αυτές οι συναρτήσεις βρίσκονται στη string.h.

errno

H errno είναι ειδική μεταβλητή του συστήματος που λαμβάνει τιμή από το σύστημα όταν αυτό δεν μπορεί να εκτελέσει κάποια εργασία. Ορίζεται στη errno.h. Οι αριθμοί σφάλματος αντιστοιχούν σε error codes μέσω μακροεντολών που περιγράφονται στο εγχειρίδιο της πρότυπης βιβλιοθήκης.

Η χρήση της errno σε ένα πρόγραμμα απαιτεί τη δήλωσή της στο πρόγραμμα ως:

   volatile int errno;

Μπορεί να τροποποιηθεί από το πρόγραμμα, αλλά αυτό είναι σπάνιο.

exit()

Η συνάρτηση exit() δηλώνεται μέσω του #include <stdlib.h> ως:

void exit(int status)

Η exit() απλά τερματίζει την εκτέλεση ενός προγράμματος  και επιστρέφει την τιμή της κατάστασης εξόδου status στο λειτουργικό σύστημα. Η τιμή status δείχνει τον κανονικό ή μή τερματισμό του προγράμματος. Συμβατικά υπάχρουν δύο προεπιλεγμένες μακροεντολές για τις τιμές επιτυχούς και ανεπιτυχούς τερματισμού:

Σε πιο σύνθετες καταστάσεις η τιμή του status μπορεί να λάβει τιμές από 0 έως 255 οι οποίες να ελέγχονται από το σύστημα κα να υπάρχει σχετική απόκριση. Συνήθως όμως απλά καλούμε exit(EXIT_FAILURE) όταν εμφανιστεί σφάλμα ή exit(EXIT_SUCCESS) για επιυχή τερματισμό.

Ροές

Οι Ροές (Streams) είναι μια αφαίρεση (abstrcation) της ανάγνωσης και εγγραφής δεδομένων ώστε αυτές να είναι σχετικά ανεξάστητες από το σύστημα, ώστε η Ε/Ε να είναι ταυτόχρονα ευέλικτη αλλά και αποδοτική.

Μια Ροή είναι είτε ένα αρχείο ή μια φυσική συσκευή (π.χ. πληκτρολόγιο ή οθόνη). Και οι δύο εκδοχές αναπαριστώνται προς το προγραμματιστή με την ίδια μορφή (αφαίρεση), δηλαδή ως ένας δείκτης σε ένα ειδικό αρχείο, τη ροή.

Στη C υπάρχει μια εσωτερική δομή δεδομένων (δείκτης σε απομονωτή αρχείου ή συσκευής), η FILE, που αναπαριστά όλες τις ροές και ορίζεται στη stdio.h. Για να επιτύχουμε Ε/Ε με ροές στη C απλά αναφερόμαστε στη κατάλληλη δομή τύπου FILE.

Απλά, στο πρόγραμμά μας δηλώνουμε ένα δείκτη σε μια δομή τύπου FILE.  Δεν απαιτούνται περισσότερες λεπτομέρειες για το τύπο του αρχείου (ή ροής).

Πριν από την εκτέλεση οποιασδήποτε λειτουργίας Ε/Ε πρέπει να ανοίξουμε (open) το αρχείο (ή τη ροή), δηλαδή να συσχετίσουμε τη λογική αφαίρεση (τον δείκτη σε δομή τύπου FILE) με ένα φυσικό αντικείμενο (π.χ. όνομα διαδρομής απλού αρχείου ή δεσμευμένο όνομα αρχείου-συσκευής).

Κατόπιν μπορούμε να προσπελάσουμε τη ροή με συναρτήσεις ανάγνωσης, εγγραφής αρχείων κλπ.

Στο τέλος του προγράμματος (συνήθως) πρέπει να  κλείσουμε τη ροή.

Η Ε.Ε ροών (άρα και αρχείων) είναι Απομονωμένη (Buffered): Αυτό σημαίνει οτι η ανάγνωση και η εγγραφή πραγματοποιείται σε σταθερά τμήματα (block) δεδομένων, τα οποία ενδιάμεσα αποθηκέυονται σε χώρο προσωρινής αποθήκευσης στη κύρια μνήμη (τον απομονωτή, buffer).  Αυτό φαίνεται στην εικόνα που ακολουθεί. Ο περιγραφέας αρχείου επομένως δείχει τον απομονωτή.

 

Μοντέλο Ε/Ε ροών και αρχείων

Αυτή η λύση οδηγεί σε αποδοτική και ευέλικτη Ε/Ε αλλά προσοχή: τα δεδομένα που γράφονται σε ένα απομονωτή δεν εμφανίζονται στο αρχείο (ή τη συσκευή) μέχρι ο απομονωτής να αδειάσει (flush). Στις συσκευές χαρακτήρων (πληκτρολόγιο, οθόνη κειμένου) αυτό γίνεται με την αλλαγή γραμμής (ENTER, $\backslash$n ). Σε συσκευές block (όπως οι δίσκοι) μπορούμε να ορίσουμε μέγεθος block ή και να επιβάλλουμε ενιδάμεσο άδειασμα του αν απαιτείται (fflush())

Προκαθορισμένες Ροές

  Στο UNIX/Linux ορίζονται 3 προκαθορισμένες ροές (στη stdio.h):

   stdin,  stdout,  stderr

Όλες είναι ροές που αντιστοιχούν σε συκευές χαρακτήρων.

Οι προεπιλεγμένες τιμές είναι η οθόνη κειμένου για stdout και stderr, το πληκτρολόγιο για το  stdin. Τα stdin και stdout  μπορούν να συνδεθούν με (ανακατευθυνθούν σε) αρχεία, προγράμματα, συσκευές Ε/Ε, κλπ. Το stderr πάντα οδηγεί στην οθόνη. Οι προεπιλεγμένες ροές είναι αυτόματα ανοικτές, δεν απαιτείται να τις ανοίγουμε στο πρόγραμμά μας. 

Μια τυπική σύνδεσή με περιγραφείς αρχείων φαίνεται πρακάτω. Το stdin έχει τη τιμή 0, το stdout 1 και το stderr 2.

/dev/stderr -> /proc/self/fd/2
/dev/stdin -> /proc/self/fd/0
/dev/stdout -> /proc/self/fd/1

Ανακατεύθυσνη

Η ανακατεύθυνση των προεπειλεγμένων ροών είναι δυνατή σε επίπεδο φλοιού UNIX/Linux στη γραμμή εντολών: 

> -- stdout προς αρχείο.

Ένα πρόγραμμα program, αντί να στείλει την έξοδο στην οθόνη τη στέλνει σε ένα αρχείο file1.

program > file1

< -- stdin από αρχείο.

Ένα πρόγραμμα program, αντί να δεχτεί είσοδο από το πληκτρολόγιο, δέχεται από το αρχείο file2.

program < file2.

| -- σωλήνωσηe: η έξοδος stdout γίνεται η είσοδος stdin του άλλου

prog1 | prog2

Βασική Ε/Ε

Πιθανότατα οι πιο κοινές συναρτήσεις είναι οι getchar() και putchar(). Όρίζονται ως εξής:

int ch;
ch = getchar();
(void) putchar((char) ch);

Παρόμοιες γενικευμένες συναρτήσεις:

int getc(FILE *stream) 
int putc(char ch,FILE *stream) 

Μορφοποιημένη Ε/Ε

printf()

 Ο ορισμός ακολουθεί:

int printf(char *format, arg list ...) -- γράφει στο stdout τη λίστα των ορισμάτων σύμφωνα με την αντίστοιχη συμβολοσειρά μορφοποίησης. Επιστρέφει τον αριθμό των χαρακτήρων που τυπώθηκαν.

Η συμβολοσειρά μορφοποίησης έχει 3 τύπους αντικειμένων:

 \e	Write an <escape> character.
\a Write a <bell> character.
\b Write a <backspace> character.
\f Write a <form-feed> character.
\n Write a <new-line> character.
\r Write a <carriage return> character.
\t Write a <tab> character.
\v Write a <vertical tab> character.
\' Write a <single quote> character.
\\ Write a backslash character.
\num Write an 8-bit character whose ASCII value is the 1-, 2-,
or 3-digit octal number num.
   
Πίνακας: προδιαγραφές μορφοποίησης
Format Spec (%) Type Result
c char single character
i,d int decimal number
o int octal number
x,X int hexadecimal number
    lower/uppercase notation
u int unsigned int
s char * print string
    terminated by $\backslash$0
f double/float format -m.ddd...
e,E " Scientific Format
    -1.23e002
g,G " e or f whichever
    is most compact
% - print % character

Μεταξύ του  % και του χαραρακτήρα ορφοποίησης μπορούμε να βάλουμε:
- (minus sign)
-- αριστερή στοίχιση.
integer number
-- δεξιά στοίχιση.
m.d
-- m = μέγεθος πεδίου, d = ψηφία μετά την υποδισατολή ή αριθμός χαρακτήρων.

Έτσι η κλήση:

printf("%-2.3f$\setminus$n",17.23478);
θα έχει ως αποτέλεσμα στην οθόνη:

17.235
ενώ η κλήση:
printf("VAT=17.5%%$\setminus$n");
εμφανίζει:
VAT=17.5%

scanf()

Η συνάρτηση αυτή είναι η αντίστοιχη της printf() για ανάγνωση και ορίζεται ως:

int scanf(char *format, args....) -- διαβάζει από το stdin και αποθηκεύσει στις μεταβλητές που δηλώνονται στη λίστα των ορισμάτων σύμφωνα με την αντίστοιχη συμβολοσειρά μορφοποίησης. Επιστρέφει τον αριθμό των χαρακτήρων που διαβάστηκαν.

Για τη μορφοποίηση ισχύουν ότι για τη printf()

Προσοχή: στη λίστα ορισμάτων της scanf() απαιτούνται διευθύνσεις των μεταβλητών ή δείκτες σε μεταβλητές.

scanf(``%d'',&i);

Θυμηθείτε οτι τα ονόματα συμολοσειρών ή πινάκων είναι δείκτες.


 char string[80];
 scanf(``%s'',string);

Αρχεία

Τα αρχεία αποτελούν τη συνηθέστερη μορφή ροής, με δεδομένο οτι ακόμη και οι συσκευές στο UNIX/Linux αναπαριστώνται ως αρχεία. 

Κατ' αρχήν πρέπει να ανοίξουμε ένα αρχείο. Η συνάρτηση fopen() είναι ως εξής:

   FILE *fopen(char *name, char *mode)

Η fopen επιστρέφει ένα δείκτη σε δομή τύπου FILE, στην ουσία είναι ο δείκτης στα περιεχόμενα του αντίστοιχου αρχείου (μέσω του απομονωτή αρχείου). Η συμβολοσειρά name είναι το όνομα διαδρομής του αρχείου που θα προσπελαστεί, έτσι ώστε το σύστημα συνδέει το συγκεκριμένο αρχείο με τον αντίστοιχο απομονωτή. Η συμβολοσειρά mode ελέγχει το τύπο προσπέλασης. Οι τύποι προσπέλασης είναι:

"r"
Μόνο ανάγνωση.
"w"
Εγγραφή. Αν ήδη υπάρχει αρχείο μηδενίζεται. Αν δεν υπάρχει αρχείο δημιουργείται.
"a"
Προσάρτηση, δηλαδή εγγραφή στο τέλος του αρχείου. Αν δεν υπάρχει αρχείο δημιουργείται.
"r+"
Ανάγνωση και εγγραφή. Τα περιεχόμενα μένουν αμετάβλητα, ο δείκτης δείχνει στην αρχή του αρχείου. Αν δεν υπάρχει αρχείο δημιουργείται.
"w+"
Εγγραφή και ανάγνωση. Αν το αρχείο υπάρχει μηδενίζεται. Αν δεν υπάρχει αρχείο δημιουργείται.  
"a+"
Προσάρτηση και ανάγνωση. Τα περιεχόμενα μένουν αμετάβλητα, ο δείκτης δείχνει στο τέλος του αρχείου. Αν δεν υπάρχει αρχείο δημιουργείται.

Αν για κάποιο λόγο το αρχείο δεν μπορεί να προσπελαστεί, επιστρέφεται ένας δείκτης NULL

Το UNIX/Linux γενικά θεωρεί όλα τα αρχεία (ροές) ως ακολουθίες bytes (χαρακτήρων), και έτσι τα χειρίζεται. Αν θέλουμε να προσπελάσουμε ένα αρχείο ή μια ροή σε μορφή δυαδική (δηλαδή οργανωμένο σε εγγραφές και block εγγραφών) τότε πρέπει να προσθέσουμε στο τέλος του αντίστοιχου τύπου προσπέλασης το b (binary), δηλαδή π.χ. αντί για "w+" έχουμε "w+b". Προσοχή οτι στη συνέχεια ένα τέτοιο αρχείο πρέπει να προσπελαύνεται με ειδικές συναρτήσεις που θα συζητηθούν παρακάτω, και όχι με αυτές που αφορούν απλή ή μορφοποιημένη προσπέλαση χαρακτήρων. 

Έτσι το άνοιγμα ενός αρχείου myfile.dat για ανάγνωση είναι:

FILE *stream, *fopen();
 /* declare a stream and prototype fopen */
stream = fopen("myfile.dat","r");

ενώ το άνοιγμα ενός δυαδικού αρχείου myfile.dat για ανάγνωση είναι:

FILE *stream, *fopen();
 /* declare a stream and prototype fopen */
stream = fopen("myfile.dat","rb");

Είναι καλή πρακτική να ελέγχουμε αν το αρχείο έχει ανοίξει σωστά:

 
if ( (stream = fopen( "myfile.dat","r")) == NULL)
{  printf(``Can't open %s$\backslash$n'',"myfile.dat");
  exit(1);
}
......

Το κλείσιμο ενός αρχείου είναι απλό:

fclose(FILE *stream)

Ανάγνωση και Εγγραφή αρχείων

Οι συναρτήσεις fprintf και fscanf χρησιμοποιούνται συχνότερα:

int fprintf(FILE *stream, char *format, args..)
int fscanf(FILE *stream, char *format, args..)

Είναι παρόμοιες με τις printf και scanf μόνο που τα δεδομένα σχετίζονται με μια ροή που πρέπει να ανοίξει με fopen(). Ο δείκτης στη ροή (ή στο αρχείο, γνωστός και ως περιγραφέας αρχείου, file descriptor) μεταβάλλεται αυτόματα με κάθε λειτουργία εγγραφής/ενάγνωσης αρχείου, έτσι ώστε δείχνει πάντα στη τρέχουσα θέση προς επεξεργασία. Δεν χρειάζεται να ανησυχούμε γι' αυτό.

char *string[80]
FILE *stream, *fopen();
 
if ( (stream = fopen(...)) != NULL)
fscanf(stream,``%s'', string);
Συναρτήσεις χαρατήρων:
 
int getc(FILE *stream), int fgetc(FILE *stream)
int putc(char ch, FILE *s), int fputc(char ch, FILE *s)

Αυτές είναι παρόμοιες με τις getchar(), putchar(). H getc() ορίζεται ως μακροεντολή στο stdio.h. H fgetc() είναι κανονική συνάρτηση βιβλιοθήκης. Έχουν το ίδιο αποτέλεσμα. 

Συναρτήσεις συμβολοσειρών:

char *fgets(char *s, int count, FILE *stream)
int fputs(char *s, FILE *s)

όπου s είναι η συμβολοσειρά και count ο μέγιστος αριθμός των προς ανάγνωση χαρακτήρων.

Υπάρχουν και αντίστοιχες συναρτήσεις για ανάγνωση/εγραφή πλήρους γραμμής (μέχρι το χαρακτήρα αλλαγής γραμμής \n) και διαχωρισμού του περιεχομένου σε υπο-συμβολοσειρές κλπ. 

Σε περίπτωση που θέλουμε να αδειάσουμε τον απομονωτή χωρίς να υπάρχει αλλαγή γραμμής:

fflush(FILE *stream) -- άδειασμα απομονωτή.

Οι γενικευμένες συναρτήσεις αρχείων (όπως η fprintf() κλπ) μπορούν να χρησιμοποιηθούν και για τις πρότυπες ροές, αφού έχουν προεπιλεγμένες τιμές περιγραφέων αρχείων.

fprintf(stderr,"Cannot Compute!!$\backslash$n");
fscanf(stdin,"%s",string);
 

Η ανάγνωση/εγγραφή σε δυαδικά αρχεία γίνεται ως εξής:

size_t fwrite (const void *data, size_t size, size_t count, FILE *stream)
size_t fread (void *data, size_t size, size_t count, FILE *stream)

όπου *data είναι δείκτης στα δεδομένα τύπου struct size_t (αριμός bytes  size σε κάθε στοιχείο δομής τύπου size_t). Επιτρέπεται η ανάγνωση/εγγραφή ενός block από count δομές size_t, δηλαδή size*count bytes.  

Είναι δυνατή η χρήση των παραπάνω συναρτήσεων σε αρχεία χαρακτήρων ως εξής:

fread (large_string, 1, 1000, stream)

Κατάσταση και Προσπέλαση Ροών

Υπάρχουν ορισμένες χρήσιμες συναρτήσεις που μας ενημερώνουν για τη κατάσταση μια ροής:

 int feof(FILE *stream);
int ferror(FILE *stream);
void clearerr(FILE *stream);
int fileno(FILE *stream);

Η χρήση τους είναι ο απλή:

feof()
-- αληθής όταν ο δείκτης  fp φθάσει στο τέλος του αρχείου (end of file).  Πχ. ανάγνωση αρχείου γραμμή προς γραμμή:
while ( !feof(fp) )
fscanf(fp,"%s",line);
ferror()
-- ελέγχει τη κατάσταση της ροής και γίνεται αληθής αν υπάρχει σφάλμα.
clearerr()
-- καθαρίζει τον έλεγχο σφάλματος μιας ροής.
int fileno()
-- επιστρέφει τον περιγραφέα αρχείου (δείκτη στη δομή τύπου FILE) μιας ροής.
Επίσης υπάρχουν ορισμένες συναρτήσεις που μας επιτρέπουν να ελέγχουμε τη θέση του fp χωρίς να πραγματοποιούμε ανάγνωση ή εγγραφή.
long int ftell(FILE *stream);
inf fseek (FILE *stream, long int offset, int whence);
int rewind
(FILE *stream);
ftell()
-- επιστρέφει τη τρέχουσα τιμή  του fp
fseek ()
-- μετακινεί τον fp κατά offset bytes, από τη θέση whence που μπορεί να πάρει τις τιμές SEEK_SET, SEEK_CUR, ή SEEK_END (πρώτη, τρέχουσα και τελευταία θέση του fp στο αρχείο). 
rewind()
-- επαναφέρει τον fp στην αρχή (για απλά αρχεία χαρακτήρων).

sprintf και sscanf

Μοιάζουν με τις fprintf και fscanf αλλά γράφουν/διαβάζουν σε συμβολοσειρές.

int sprintf(char *string, char *format, args..)
int sscanf(char *string, char *format, args..)

Παράδειγμα:

float full_tank = 47.0; /* litres */
float miles = 300;
char miles_per_litre[80];

sprintf(miles_per_litre,``Miles per litre= %2.3f'', miles/full_tank);

Ε/Ε Χαμηλού Επιπέδου

Στην Ε/Ε Χαμηλού Επιπέδου δεν  υπάρχει απομονωτής-- κάθε λειτουργία ανάγνωσης/εγγραφής καταλήγει σε άμεση προσπέλαση της αντίστοιχης συσκευής (πληκτρολογίου, οθόνης, δίσκου κλπ), και την άμεση μεταφορά του συγκεκριμένου αριθμού bytes.

Επίσης δεν υπάρχει μορφοποίηση, μιλούμε πάντα για απλά bytes. Επομένως όλα τα αρχεία αντιμετωπίζονται ως δυαδικά, (επίθεμα b στο fopen()) και όχι ως αρχεία κειμένου.

Ο δείκτης σε τύπο FILE αντικαθίσταται με το χαμηλού επιπέδου αριθμητικό αντίστοιχό του (περιγραφέα αρχείου, file descriptor/hadle) που ουσιαστικά αντιστοιχεί σε έναν ακέραιο. Θυμηθείτε οτι το stdin αντιστοιχεί στο 0, το stdout στο 1 και το stderr στο 2

Άνοιγμα αρχείου σε χαμηλό επίπεδο:

int open(char *filename, int flag, mode_t mode) -- επιστρέφει τον περιγραφέα ή -1 για αποτυχία. Αντί για -1 μπορεί να ρυθμιστεί να επιστρέφει errno για κωδικούς λαθών σχετικών με αρχεία

Το flag ελέγχει τους τύπους προσπέλασης, όπως καθορίζεται στη fcntl.h:

O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_WRONLY + ... Πρόκειται για bit mask που ρυθμίζει τη δημιουργία και τη λειτουργία των αρχείων, (δικαιώματα, άδειες κλπ.) Δείτε τις σχετικές σελίδες τεκμηρίωσης.

mode -- συνήθως 0 στις περισσότερες εφαρμογές.

Η creat() cδεν χρησιμοποιείται πλέον.

Κλείσιμο αρχείου:

int close(int handle)  

Ανάγνωση/Εγγραφή:

int read(int handle, char *buffer, unsigned length)
int write(int handle, char *buffer, unsigned length)

διαβάζουν/γράφουν length αριθμό από bytes από/προς το αρχείο (handle) προς/από τη θέση μνήμης buffer.

Η συνάρτηση sizeof() συνήθως χρησιμοποιείται στο length για να μεταφράσει τύπους και δομές δεδομένων σε bytes.

Οι read() και write() επιστρέφουν τον αριθμό των bytes ή -1 για αποτυχία.

Παράδειγμα:

/* program to read a list of floats from a binary file */
/* first byte of file is an integer saying how many */
/* floats in file. Floats follow after it, File name got from */
/* command line */
 
#include<stdio.h>
#include<fcntl.h>
 
float bigbuff[1000];
 
main(int argc, char **argv)
 

int fd;
int bytes_read;
int file_length;
 
if ( (fd = open(argv[1],O_RDONLY)) = -1)
{ /* error file not open */....
perror("Datafile");
exit(1);
}
if ( (bytes_read = read(fd,&file_length, sizeof(int))) == -1)
{ /* error reading file */...
exit(1);
}
if ( file_length > 999 )
{/* file too big */ ....
}
if ( (bytes_read = read(fd,bigbuff, file_length*sizeof(float))) == -1)
{ /* error reading open */...
exit(1);
}

}

Η συνάρτηση fdopen() επιτρέπει τη σύνδεση ενός περιγραφέα αρχείου handle με ένα δείκτη σε αρχείο (ροή), έτσι ώστε οι δύο μέθδοι προσπέλασης αρχείων (υψηλού και χαμηλού επιπέδου) μπορούν να αναμιχθούν.  Η συμβολοσειρά mode έχει την ίδια σύνταξη και σημασία όπως στη συνάρτηση fopen().

 FILE *fdopen(int handle, char *mode).

Ασκήσεις

Άσκηση 12573

Γράψτε ένα πρόγραμμα που να αντιγράφει ένα αρχείο σε άλλο. Τα ονόματα των δύο αρχείων δίνονται ως ορίσματα. Η αντιγραφή να γίνεται σε blocks των 512 bytes. Να γίνουν όλοι οι έλεγχοι ορισμάτων, ανοίγματος και προσπέλασης αρχείων κλπ.

Άσκηση 12577

Γράψτε ένα πρόγραμμα που να εμφανίζει τις τελευταίες n γραμμές από ένα αρχείο κειμένου, όπου το όνομα του αρχείου δίνονται ως ορίσματα. Το n ως προεπιλογή είναι 5, αλλά μπορεί να τροποποιηθεί με μια επιλογή στη μορφή

	last -n file.txt

όπου το n είναι κάποιος ακέραιος. Σκεφτείτε όσο το δυνατό περισσότερους ελέγχους.

Άσκηση 12578

Γράψτε ένα πρόγραμμα που συγκρίνει δύο αρχεία κειμένου και εμφανίζει τις γραμμές που διαφέρουν. Χρησιμοποιείστε τις κατάλληλες συναρτήσεις σύγκρισης συμβολοσειρών.


Dave Marshall
1/5/1999
μετάφραση και προσαρμογή στα Ελληνικά Κ.Γ. Μαργαρίτης
7/3/2008