This is an old revision of the document!
Table of Contents
Είσοδος και έξοδος αρχείων
Η C++ εισάγει την έννοια του stream, ως μία σειρά από χαρακτήρες οι οποίοι μπορούν:
- να γραφούν σε ένα αρχείο ή σε μία σειριακή συσκευή με τη σειρά που αυτοί εγγράφονται στο stream.
- να διαβαστούν από ένα αρχείο ή από μία σειριακή συσκευή με τη σειρά που είναι αποθηκευμένοι ή με τη σειρά που εισήχθησαν στη συσκευή.
Βασικά χαρακτηριστικά των streams είναι τα εξής:
- Ένα stream μεταφράζεται πάντα σε μία ακολουθία χαρακτήρων. Τα streams δίνουν ένα ενιαίο interface προς τα αντικείμενα για οποιοδήποτε μέσω αποθήκευσης. Εάν μία κλάση μπορεί να γράψει σε ένα stream τότε μπορεί να γράψει σε όλα τα πιθανά streams.
- Τα streams δουλεύουν αυτόματα με τους βασικούς τύπους δεδομένων. Για τους αναφορικούς τύπους μπορείτε να υπερφορτώσετε τους τελεστές >> και << για είσοδο από το stream και έξοδο προς το stream αντίστοιχα. Η χρήση των τελεστών >> και << απλοποιεί εξαιρετικά τον τρόπο με τον οποίο αλληλεπιδρούν τα προγράμματα με τα streams.
Η C++ παρέχει τις παρακάτω κλάσεις για είσοδο και έξοδο από αρχεία.
- ifstream: Κλάση για διάβασμα από αρχείο.
- ofstream: Κλάση για γράψιμο σε αρχείο.
- http://www.cplusplus.com/reference/fstream/fstream/|fstream]]: Κλάση για διάβασμα και γράψιμο σε αρχείο.
Οι παραπάνω κλάσεις είναι απόγονοι των κλάσεων istream και ostream. Η χρήση των παραπάνω κλάσεων είναι ανάλογη με τις κλάσεις std::cin και std::cout που επίσης είναι απόγονοι των κλάσεων istream και ostream. Δείτε ενδεικτικά, το παρακάτω παράδειγμα γραφής στο αρχείο hello.txt.
- hello_file.cpp
#include <iostream> #include <fstream> using namespace std; int main() { ofstream myfile; myfile.open ("hello.txt"); myfile << "Hello World!"; myfile.close(); }
Άνοιγμα αρχείου για διάβασμα και γράψιμο
Το άνοιγμα των αρχείων γίνεται μέσω της μεθόδου open η οποία προδιαγράφεται παρακάτω:
void open (const char* filename, ios_base::openmode mode);
Η παράμετρος filename είναι ένα αλφαριθμητικό που περιγράφει το σχετικό ή απόλυτο path προς το αρχείο. Η παράμετρος mode μπορεί να πάρει τις ακόλουθες τιμές ή συνδυασμούς τους:
Τιμή | Επεξήγηση |
---|---|
ios::in | Άνοιγμα για διάβασμα από το αρχείο. |
ios::out | Άνοιγμα για γράψιμο στο αρχείο. |
ios::binary | Άνοιγμα για διάβασμα ή γράψιμο σε δυαδικό αρχείο. |
ios::ate | Θέτει τη θέση γραψίματος στο τέλος του αρχείου. Εάν δεν προσδιοριστεί το συγκεκριμένο flag η θέση γραψίματος τίθεται στην αρχή. |
ios::app | Η θέση γραψίματος τίθεται στο τέλος του αρχείου, προσθέτοντας πληροφορία (append) στο υφιστάμενο αρχείο. |
ios::trunc | Εάν το αρχείο υπάρχει ήδη και έχει περιεχόμενο, το υφιστάμενο περιεχόμενο διαγράφεται. |
Οι παραπάνω τιμές μπορούν να συνδυαστούν ως εξής:
ofstream lena; // Open lena.png for writing in binary mode. // Truncate file contents if file previously exists. lena.open("lena.png", ios::out | ios::trunc | ios::binary);
Default τιμές
Οι default τιμές της παραμέτρου mode για τις παραπάνω κλάσεις κλάσεις έχουν ως εξής:
Κλάση | Τιμή | Παρατήρηση |
---|---|---|
ofstream | ios::out | Η τιμή ισχύει ακόμη και εάν αλλάξετε την παράμετρο mode |
ifstream | ios::in | Η τιμή ισχύει ακόμη και εάν αλλάξετε την παράμετρο mode |
fstream | ios::in | ios::out | Η τιμή δεν ισχύει εάν αλλάξετε την παράμετρο mode. Μπορείτε να επαναορίσετε τις τιμές |
Για να ελέγξετε εάν ένα αρχείο έχει ανοίξει σωστά για διάβασμα μπορείτε να χρησιμοποιήσετε τη μέθοδο is_open() ως εξής:
ofstream lena("lena.png", ios::out | ios::trunc | ios::binary); if (!lena.is_open()) { cerr << "Error while opening \"lena.png\" for writting\n"; }
Παρατηρήστε ότι στο παραπάνω παράδειγμα χρησιμοιούμε τον κατασκευαστή της κλάσης ofstream αντί για τη μέθοδο open. Τα ορίσματα που λαμβάνει ο κατασκευαστής και η σειρά τους είναι ταυτόσημα με αυτά της μεθόδου open.
Κλείσιμο αρχείου
Για να κλείσετε το αρχείο που προηγουμένως ανοίξατε με τη μέθοδο open αρκεί να καλέσετε τη μέθοδο close ως εξής:
lena.close();
Αρχεία κειμένου
Streams κειμένου είναι εκείνα τα streams που κατά το άνοιγμα του αρχείου δεν έχουν ορισμένο το flag ios::binary. Τα αρχεία αυτά υποθέτουμε ότι περιέχουν κείμενο και κατ' επέκταση μπορούμε να τα διαβάσουμε υποθέτοντας ότι οι λέξεις χωρίζονται μεταξύ τους από κενό χαρακτήρα ή χαρακτήρα αλλαγής γραμμής (' ','\t','\n','\x0B','\f','\r
') και οι γραμμές χωρίζονται με χαρακτήρα αλλαγής γραμμής ('\r','\n
').
Ο τρόπος με τον οποίο γράφετε ή διαβάζετε είναι ο τρόπος που το κάνετε για τη βασική είσοδο και έξοδο.
Γράψιμο σε αρχείο κειμένου |
---|
|
Διάβασμα από αρχείο κειμένου γραμμή-γραμμή | Διάβασμα από αρχείο κειμένου λέξη-λέξη και εκτύπωση κάθε λέξης σε νέα γραμμή |
---|---|
|
|
Έλεγχος της εσωτερικής κατάστασης του stream
Κάθε stream διαθέτει μία σειρά από flags που ενημερώνουν για την κατάσταση του. Τα flags αυτά αποθηκεύονται στην μεταβλητή std::iosbase::iostate. Συγκεκριμένα η μεταβλητή περιέχει τα παρακάτω flags.
flag | Περιγραφή |
---|---|
eofbit | End-Of-File reached while performing an extracting operation on an input stream. |
failbit | The last input operation failed because of an error related to the internal logic of the operation itself. |
badbit | Error due to the failure of an input/output operation on the stream buffer. |
goodbit | No error. Represents the absence of all the above (the value zero). |
Οι ακόλουθες μέθοδοι ενημερώνουν για την εσωτερική κατάσταση του stream επιστρέφοντας μία boolean τιμή ως εξής:
bool good() const; | Επιστρέφει true εάν κανένα από τα error flags (failbit, badbit, eofbit) δεν έχει τιμή true. |
bool bad() const; | Ελέγχει εάν το badbit είναι true. Το badbit λαμβάνει τη τιμή true εάν συμβεί κάποιο μη αναστρέψιμο σφάλμα στο εσωτερικό του stream. Εάν το badbit γίνει true οποιαδήποτε ενέργεια πάνω στο stream θα πρέπει να αποκλειστεί. |
bool eof() const; | Ελέγχει εάν το eofdbit είναι true. To eofbit γίνεται true, όταν η διαδικασία ανάγνωσης φτάνει στο τέλος του αρχείου. |
bool fail() const; | Ελέγχει εάν το failbit ή το badbit έχουν τιμή true. Το failbit λαμβάνει τη τιμή true εάν συμβεί κάποιο λάθος στη χρήση του stream. Το λάθος δεν θεωρείται μη αναστρέψιμο και το stream είναι πιθανό ότι μπορεί να χρησιμοποιηθεί για επιπλέον λειτουργίες εισόδου ή εξόδου. |
Αλλαγή της θέσης ανάγνωσης ή εγγραφής στο stream
Τα streams εισόδου διατηρούν τη θέση ανάγνωσης και τα strams εξόδου τη θέση εγγραφής. Streams που υποστηρίζουν είσοδο και έξοδο διατηρούν ξεχωριστές θέσεις για ανάγνωση και για εγγραφή.
Ανάγνωση της θέσης ανάγνωσης ή εγγραφής
Η κλάση istream διαθέτει τη συνάρτηση tellg για την ανάγνωση της θέσης ανάγνωσης μέσα στο stream. Η συνάρτηση επιστρέφει ένα αντικείμενο του τύπου streampos και ορίζεται ως εξής:
streampos tellg();
Η κλάση ostream διαθέτει τη συνάρτηση tellp για την ανάγνωση της θέσης εγγραφής μέσα στο stream. Η συνάρτηση επιστρέφει ένα αντικείμενο του τύπου streampos και ορίζεται ως εξής:
streampos tellp();
Αλλαγή της θέσης ανάγνωσης ή εγγραφής
Η κλάση istream διαθέτει τη συνάρτηση seekg για την αλλαγή της θέσης ανάγνωσης μέσα στο stream. Η συνάρτηση επιστρέφει μία αναφορά στο υφιστάμενο stream και ορίζεται ως εξής:
istream& seekg (streampos pos); istream& seekg (streamoff off, ios_base::seekdir way);
Η κλάση ostream διαθέτει τη συνάρτηση seekp για την αλλαγή της θέσης εγγραφής μέσα στο stream. Η συνάρτηση επιστρέφει μία αναφορά στο υφιστάμενο stream και ορίζεται ως εξής:
ostream& seekp (streampos pos); ostream& seekp (streamoff off, ios_base::seekdir way);
Οι παράμετροι που λαμβάνουν οι συναρτήσεις seekg και seekp είναι οι εξής:
- pos: Απόλυτη θέση μέσα στο stream ξεκινώντας από την αρχή.
- off: Σχετική θέση μέσα στο stream. Συνδέεται με την τιμή της μεταβλητής way.
- way: Μπορεί να πάρει μία από τις παρακάτω τιμές
ios::beg | αρχή του stream |
ios::cur | τρέχουσα θέση του stream |
ios::end | τέλος του stream |
Ακολουθούν δύο παραδείγματα αλλαγής της θέσης ανάγωσης και εγγραφής πάνω στο file stream.
- fileseek.cpp
#include <iostream> #include <fstream> using namespace std; int main (int argc, char *argv[]) { string filename; cout << "Enter output filename: "; cin >> filename; ofstream outfile(filename.c_str(), ios::trunc); if (!outfile.is_open()) { cout << "[Write] Unable to open " << filename; return -1; } outfile << "I hate pointers in C"; outfile.flush(); ifstream infile(filename.c_str()); if (!infile.is_open()) { cout << "[Read] Unable to open " << filename; return -1; } string line; getline( infile, line ); cout << line << endl; outfile.seekp(2); outfile << "love"; outfile.seekp(1, ios::cur); outfile << "references"; outfile.seekp(0,ios::end); outfile << "++" << endl; infile.seekg(0, ios::beg); getline( infile, line ); cout << line << endl; }
- filesize.cpp
#include <iostream> #include <fstream> using namespace std; int main () { streampos begin,end; string filename; cout << "Enter filename: "; cin >> filename; ifstream file(filename.c_str()); if (!file.is_open()) { cout << "Unable to open " << filename; return -1; } begin = file.tellg(); file.seekg (0, ios::end); end = file.tellg(); myfile.close(); cout << "file size is: " << (end-begin) << " bytes.\n"; }
Δυαδικά αρχεία
Για δυαδικά αρχεία η ανάγνωση και η εγγραφή με χρήση των τελεστών << (extraction operator) και >> (insertion operator) δεν είναι εφικτή. Για τον λόγο αυτό τα file streams περιέχουν δύο μεθόδους για διάβασμα και γράψιμο δυαδικής πληροφορίας.
istream& read (char* s, streamsize n); | Διάβασμα n στοιχείων τύπου char από το stream και αποθήκευση στο s. Η συηνάρτηση επιστρέφει μία αναφορά στο υφιστάμενο stream. Σε περίπτωση που δεν υπάρχουν n διαθέσιμα bytes στο stream αντιγράφει στο s όσα είναι διαθέσιμα και θέτει τα flags failbit και eofbit. |
ostream& write (const char* s, streamsize n); | Γράψιμο n στοιχείων τύπου char από το s προς το stream. |
Δείτε το παρακάτω παράδειγμα ανάγνωσης και εγγραφής από δυαδικό αρχείο. Το πρόγραμμα αντιγράφει το περιεχόμενο ενός αρχείου σε ένα άλλο byte-byte.
- binary_copy.cpp
#include <iostream> #include <fstream> #include <cmath> using namespace std; #define SIZE 1 int main (int argc, char *argv[]) { string filename; cout << "Enter input filename: "; cin >> filename; ifstream infile(filename.c_str()); if (!infile.is_open()) { cout << "[Read] Unable to open " << filename; return -1; } cout << "Enter output filename: "; cin >> filename; ofstream outfile(filename.c_str(), ios::trunc); if (!outfile.is_open()) { cout << "[Write] Unable to open " << filename; return -1; } char buf[SIZE]; while(!infile.eof()) { infile.read(buf, SIZE); outfile.write(buf, SIZE); } infile.close(); outfile.close(); }