User Tools

Site Tools


cpp:file_io

This is an old revision of the document!


Είσοδος και έξοδος αρχείων

Η C++ εισάγει την έννοια του stream, ως μία σειρά από χαρακτήρες οι οποίοι μπορούν:

  • να εισαχθούν στο stream με συγκεκριμένη σειρά και να γραφούν σε ένα αρχείο ή σε μία σειριακή συσκευή με τη σειρά που εισήχθησαν.
  • να διαβαστούν από ένα αρχείο ή από μία σειριακή συσκευή με τη σειρά που είναι αποθηκευμένοι ή με τη σειρά που εισήχθησαν στη συσκευή.

Βασικά χαρακτηριστικά των streams είναι τα εξής:

  1. Ένα stream μεταφράζεται πάντα σε μία ακολουθία χαρακτήρων. Τα streams δίνουν ένα ενιαίο τρόπο γραφής και ανάγνωσης προς τα αντικείμενα που τα χρησιμοποιούν για οποιοδήποτε μέσο επικοινωνίας/αποθήκευσης. Εάν μία κλάση μπορεί να γράψει σε ένα stream τότε μπορεί να γράψει σε όλα τα πιθανά streams.
  2. Τα streams δουλεύουν αυτόματα με τους βασικούς τύπους δεδομένων. Για τους αναφορικούς τύπους μπορείτε να υπερφορτώσετε τους τελεστές >> και << για είσοδο από το stream και έξοδο προς το stream αντίστοιχα. Η χρήση των τελεστών >> και << απλοποιεί εξαιρετικά τον κώδικα για να γράψουμε σε ένα stream ή να διαβάσουμε από αυτό.

Η C++ παρέχει τις παρακάτω κλάσεις για είσοδο και έξοδο από αρχεία.

  1. ifstream: Κλάση για διάβασμα από αρχείο.
  2. ofstream: Κλάση για γράψιμο σε αρχείο.
  3. 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. Μπορείτε να επαναορίσετε τις τιμές

Παρατηρήστε ότι η μέθοδος open δεν έχει επιστρεφόμενη τιμή για να ελέγξουμε την αποτυχία, ούτε παράγει κάποιο exception. Για να ελέγξετε εάν ένα αρχείο έχει ανοίξει σωστά για διάβασμα ή γράψιμο μπορείτε να χρησιμοποιήσετε τη μέθοδο 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').

Ο τρόπος με τον οποίο γράφετε ή διαβάζετε είναι ο τρόπος που το κάνετε για τη βασική είσοδο και έξοδο.

Γράψιμο σε αρχείο κειμένου
file_write.cpp
#include <iostream>
#include <fstream>
using namespace std;
 
int main (int argc, char *argv[]) {
  string filename;
  cout << "Enter output filename: ";
  cin >> filename;
  ofstream myfile (filename.c_str());
  if (!myfile.is_open()) {
    cout << "Unable to open file " << filename;
    return -1;    
  }
  myfile << "- Which OO language do you prefer?.\n";
  myfile << "- My favorite is C++.\n";
  myfile << "- I prefer Java and Python.\n";
  myfile.close();
}
Διάβασμα από αρχείο κειμένου γραμμή-γραμμή Διάβασμα από αρχείο κειμένου λέξη-λέξη και εκτύπωση κάθε λέξης σε νέα γραμμή
file_read_line_by_line.cpp
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
 
#define MAX_LINE_LEN 1024 // really big!
 
int main () {
  string filename;
  cout << "Enter input filename: ";
  cin >> filename;
  ifstream myfile (filename.c_str());
  char line[MAX_LINE_LEN]; 
 
  if (!myfile.is_open()) {
    cout << "Unable to open file " << filename;
    return -1;    
  }
  while ( myfile.getline (line, MAX_LINE_LEN) )
    cout << line << '\n';   
  myfile.close();  
}
file_read_word_by_word.cpp
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
 
int main () {
  string filename, word;
  cout << "Enter input filename: ";
  cin >> filename;
  ifstream myfile (filename.c_str());
 
  if (!myfile.is_open()) {
    cout << "Unable to open file " << filename;
    return -1;    
  }
  while ( !myfile.eof() )  {
    myfile >> word;
    cout << word << endl; 
  }
  myfile.close();
 
}

Έλεγχος της εσωτερικής κατάστασης του 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 εισόδου διατηρούν τη θέση ανάγνωσης και τα streams εξόδου τη θέση εγγραφής. 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();
}
cpp/file_io.1526880095.txt.gz · Last modified: 2018/05/21 04:21 (external edit)