Προς το περιεχόμενο

C++ file I/O


freskas

Προτεινόμενες αναρτήσεις

Δημοσ.

Εχω το εξης προβλημα:

Αποθηκευω καποια στοιχεια σε αρχειο στην εξης μορφη:

>
ena duo 
tria tessera

 

Για να αλλαξω ενα στοιχειο (το tessera πχ να το κανω tria) κανω:

>
fstream File("file.txt", ios::in | ios:: out);
File.seekg(0,ios::beg);
while (File.eof() == false)
{
string temp;
File >> temp;
if (temp == tria)
{
	File.seekp(File.tellg(),ios::beg);
	File << tria;
}
}
File.flush();
File.close();

 

Το προβλημα ειναι οτι το tessera γινεται πλεον triaera

Δλδ επειδη ειναι μικροτερο σε μηκος κολλαει με την προυγουμενη εγγραφη.

Ποιος ειναι ο σωστος τροπος για να γινει το παραπανω?

 

Επισης χρησιμοποιω strings οποτε το μεγεθος τους κυμαινεται σε καθε εγραφη.

Αρα δεν ειμαι σιγουρος πως να χρησιμοποιησω την seekg

(ποσα bytes δλδ να παω απο την αρχη του εγραφου η απ το τελος του για να βρω την καταλληλη θεση)

Δημοσ.

Εκανα το εξης:

>
fstream File("file.txt", ios::in | ios:: out);
File.seekg(0,ios::beg);
while (!File.eof())
{
string temp;
sting toput = "tria";
File >> temp;
if (temp == tessera)
{
	File.seekg(-sizeof(temp),ios::cur);
	File.seekp(File.tellg(),ios::beg);
	if (temp > toput)
		File << setw(sizeof(temp)) << toput;
	else
		File << toput;
}
}
File.flush();
File.close();

κανει compile αλλα δεν φαινεται να δουλευει, το αρχειο file.txt δεν αλλαζει μενει ως εχει.

Ευχαριστω για την ιδεα, αν εχεις καμια ακομα μη δυστασεις ;)

Απλα νομιζω οτι αν ηθελα να αλλαξω το tria σε xiliaoxtakosiapenintae3i θα ειχα αλλο προβλημα με αυτη τη μεθοδο (θα σβηνοταν και το tessera), αρα κατι αλλο θελει γι αυτην την περιπτωση.

Με καποιον τροπο καθε σειρα να επανεγγραφεται μετα απο την αλλαγη.

Νομιζω οτι πρεπει να κανω:

>
getline(File, line)
line >> toput1 >> toput2;
string toput2 = "tria";
File.seekg(-sizeof(line),ios::cur);
bank << toput1 << toput2;
File.seekp(File.tellg(),ios::beg);
File << line;

κατι τετοιο...

 

απλα δεν μου δουλευει το line >> toput1 >> toput2

δλδ πως ενα string με κενα το ξεχωριζουμε σε πολλα strings ?

καποια βοηθεια επι τουτου?

 

Απλα εγω εχω βαλει 5 στοιχεια ανα γραμμη μεταβλητου μεγεθους χωριζομενα με κενο:

>
username password realname accoundID balance
username1 password1 realname1 accoundID1 balance1
...

και θελω να αλλαζω καποια απο αυτα. Αυτο θελω να κανω. Δεν ξερω αν εγινα κατανοητος...

Να φορτωσω ολο το αρχειο σε ενα πινακα στη μνημη νομιζω πως μπορω να το κανω, να το απο συνθεσω σε tokens ομως...

Για να βρω τι θελω ν'αλλαξω κανω το εξης:

εστω οτι θελω να αλλαξω το balance1:

κανω ενα search για το username1 και εφοσον το βρω αποθηκευω τα υπολοιπα μεχρι το accoundID αρα ο pointer του get ειναι στο balance βαζω τον pointer του put ισο με τον get με seekp και γραφω τη νεα τιμη. Απλα σκεφτομαι οτι θα ηταν καλλητερο να ξαναγραφω ολοκληρη τη γραμμη. Μιλαμε για ενα χρηστη τη φορα τα παραπανω.

Αν μπορουσες να μου πεις πως ενα string με κενα το αποσυνθετουμε σε πολλα μικροτερα νομιζω θα λυνοταν το προβλημα ;)

 

Νομιζω βρηκα λυση για το παραπανω με την stringstream

Μπα false alarm

Δημοσ.

Το παράδειγμα που παραθέτεις δεν είναι αρκετό για να σε συμβουλέψει κάποιος. Δεν αποτελεί αυτό που λέμε "real life application". Θα μπορούσες να γράφεις και το null terminator, οπότε το πεδίο θα γινόταν tria\0ra\0 και δεν θα είχες πρόβλημα όταν η νέα εγγραφή είναι μικρότερη από την παλιά. Αλλά αυτό δεν είναι λύση σε αόριστο πρόβλημα.

 

Γενικά, υπό το πρίσμα μια εφαρμογής I/O, έχουμε δύο τύπους αρχείων:

 

- τα δομημένα, πχ βάσεις δεδομένων, αρχεία media, και

- τα ελεύθερα (free form), πχ αρχεία κειμένου

 

τα δομημένα διακρίνονται σε FLR (Fixed Length Record) και VLR (Variable Length Record).

 

Το seek εφαρμόζεται σε δομημένα αρχεία γιατί μόνον αυτά μπορούν να είναι random accessed. Τα ελεύθερα τα δουλεύουμε στη μνήμη, σαν ένα μεγάλο string. Είτε φορτώνοντάς τα μια κι έξω είτε εφαρμόζοντας τεχνικές memory mapped I/O. Επίσης, είναι εξαιρετικά δύσκολο και "ακριβό" ένα ελεύθερο αρχείο να επεξεργάζεται από περισσότερους του ενός χρήστες (ή threads) ταυτόχρονα. Γι αυτό και σε περιπτώσεις Multi-user χρησιμοποιούμε δομημένα αρχεία, ακόμη και αν πρόκειται για αρχεία κειμένου. Το αρχείο κειμένου μετατρέπεται σε δομημένο εάν θεωρήσουμε πως η ελάχιστη μονάδα επεξεργασίας δεν είναι ο χαρακτήρας αλλά ένα σύνολο χαρακτήρων που το λέμε "σελίδα" (page). Διαθέτοντας έναν header με δείκτες, κλπ, φτιάχνουμε το ανάλογο ενός FAT.

 

Στη δική σου περίπτωση, αν υποθέσουμε πως το αρχείο αποτελείται από μεταβλητού μήκους tokens, space delimited, και το μέγεθος είναι της τάξης των KB, τότε το φορτώνεις στη μνήμη, το αποσυνθέτεις μετατρέποντάς το σε πίνακα από tokens, κάνεις την επεξεργασία σου, το επανασυνθέτεις και το σώνεις. Όλα αυτά γίνονται σε ms.

 

Αν τα tokens δεν είναι μεταβλητού μήκους (το μήκος όλων είναι προκαθορισμένο max) τότε φυσικά μπορείς και να χρησιμοποιήσεις seek και να γράφεις κατευθείαν την τιμή πάνω στο αρχείο ή null terminated ή space padded.

 

Παραμένει το πρόβλημα του πως ξέρεις ποιο token/πεδίο θέλεις να αλλάξεις. Αυτό δεν μας το εξήγησες, οπότε σε παραπέμπω στην αρχή του μηνύματος.

Δημοσ.
Εχω το εξης προβλημα:

Αποθηκευω καποια στοιχεια σε αρχειο στην εξης μορφη: [..]

 

Αυτό που ζητάς μπορεί να γίνει με την βοήθεια των εντολών που μας παρέχει η string class της STL (και φυσικά η fstream). Συγκεκριμένα μπορείς να φορτώσεις (fstream.rdbuf) το αρχείο κειμένου σε ένα string class, ύστερα να βρεις την θέση του string που θες να αλλάξεις (string.find), να το διαγράψεις (string.erase) και να προσθέσεις (string.insert) στην θέση του το νέο.

 

Ακολουθεί ένα απλό παράδειγμα γραμμένο σε ET/CodeGear C++ Builder (το διευκρινίζω διότι καμιά φορά υπάρχουν προβλήματα μεταξύ των C++ compilers), το οποίο αλλάζει το πρώτο tesera που θα βρει (από ένα αρχείο TEST.TXT) σε xiliaoxtakosiapenintae3i γράφοντας στην οθόνη τα αποτελέσματα της αλλαγής (για περισσότερες λεπτομέρειες διάβασε τα σχόλια που περιέχει –θεωρώ ότι είναι αρκετά για να συνεχίσεις μόνος σου):

 

>
//-Simple replace string (directx)-------------------------------------------
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>

#ifdef __BORLANDC__	//	Ignore them (they are C++ Builder compiler options)
#pragma hdrstop
#endif
//---------------------------------------------------------------------------

#ifdef	__BORLANDC__
#pragma argsused
#endif
int main(int argc, char *argv[])
{
//	Open file for read & write
std::fstream	fStream("TEST.TXT");
if(!fStream.is_open())
	std::cout << "Cannot open file for I/O" << std::endl;
else
 {
	//	Load file stream into stringstream and then convert it to string
	std::stringstream	ssStream;
	ssStream << fStream.rdbuf();
	std::string	strBuffer = ssStream.str();

	/*
	 *	strSource => string to find/replace
	 *	strDestination => string to replace with ...
	 */
	std::string	strSource = "tesera",
				strDestination = "xiliaoxtakosiapenintae3i";

	//	Find the first occurance of strSource and replace it to strDestination
	size_t	Position = strBuffer.find(strSource.c_str(), 0, strSource.size());
	if(Position == std::string::npos)
		std::cout << strSource << " string not found!" << std::endl;
	else
	 {
		/*
		 *	Replace string:
		 *		1. Erase Source string
		 *		2. Insert new string in erased Source position
		 */
		strBuffer.erase(Position, strSource.size());
		strBuffer.insert(Position, strDestination);

		//	Echo changes on stdout
		std::cout << strBuffer;
	 }

       //	Release file stream
	fStream.close();
 }

//	End of program
   std::cout << std::endl << "Press Enter to exit..";
std::cin.get();
return 0;
}
//---------------------------------------------------------------------------

 

Είσοδος:

 

>
ena duo
tria tesera

 

Έξοδος:

>
ena duo
tria xiliaoxtakosiapenintae3i

Press Enter to exit..

 

Το παράδειγμα αυτό δεν είναι φυσικά απόλυτο, καθώς δεν χρησιμοποιώ συχνά την βιβλιοθήκη της C++ για I/O αλλά ούτε και την C++ string class (χρησιμοποιώ άλλες ειδικές βιβλιοθήκες του C++ Builder) οπότε μπορεί να περιέχει bugs ή μπορεί να γραφθεί σαφώς καλύτερα.

 

Καλή συνέχεια.

Δημοσ.

Ευχαριστω παιδια για την βοηθεια,

δοκιμασα τη λυση σου Directx με καποιες παραλλαγες αλλα δεν πετυχε, μου crashαρει το προγραμμα.

Απ οτι καταλαβα δεν αποθηκευεις την αλλαγη στο αρχειο στον παραπανω κωδικα απλα με ενα fStream << strBuffer; γινεται αυτο.

Το προβλημα με την λυση αυτη ειναι οτι αν εχεις πχ 2 καταχωρισεις balance με την ιδια τιμη θα αλλαξει η πρωτη που θα πετυχει η find οποτε μπορει να ειναι και λαθος.

μεχρι στιγμης (θεωρητικα) σαν λυση βρηκα το παρακατω:

>
string username, password, realname, accoundID, line;
double new_balance, balance;
bool check = false;

fstream bank("file.txt", ios::in | ios::out);
bank.seekg(0,ios::beg);

while (!bank.eof() && check == false)
{
getline(bank,line);
stringstream ss(line);
ss >> username >> password >> realname >> accoundID >> balance;

if (username == aclient.getUserName())
	check = true;
else
	check = false;
}

if (check == true)
{
bank.seekg(-line.length(),ios::cur);
bank.seekp(bank.tellg(),ios::beg);
bank << username << " " << password << " " << realname << " " << accoundID << " " << new_balance;
bank.flush();
bank.close();
}

αλλα δεν δουλευει, δεν γραφει στο αρχειο καθολου και δεν καταλαβαινω το γιατι (καπου κοβει βολτες ο pointer και δεν παει στο σωστο σημειο :P)

Δημοσ.

Ενταξει, εβγαλα ακρη τελικα

(αποθηκευοντας ολο το αρχειο στη μνημη, κανοντας τις αλλαγες και ξανα αποθηκευοντας το στο δισκο, οπως ειπε ο fromaz)

 

Το μονο που μενει ειναι να κραταω τις τελευταιες 5 συναλαγες και οταν γεμιζει η λιστα να σβηνεται η παλιοτερη:

κατι τετοιο δλδ:

transaction -> |4| |3| |2| |1| |0|

 

να γινεται ενα shift right δλδ και το 0 να πετιεται.

Υπαρχει ετοιμο κατι τετοιο στην C++? stack θελει?

 

Και 2 τελευταια:

-Εχω ενα menu επιλογων στο οποιο αν δωσεις αλφαριθμητικο αντι για αριθμο, κολλαει και δειχνει συνεχεια το ιδιο menu.

Κατι με try και catch δεν πρεπει να κανω? (αλλα πως γινεται αυτο)

Δημοσ.

Σκεφτηκα να κανω αυτο:

>char choise[2] = "\0";
int alpha;
cin.get(choise,2);
alpha = atoi(choise);
switch (alpha){
//rest of the code
}

 

δλδ να παιρνω unformated την εισοδο να την μετατρεπω σε ακεραιο και να την περναω απο switch

αλλα δεν μου δουλευει, καμια ιδεα?

 

edit:

δουλεψε με cin >> choise;

Δημοσ.

Πραγματικά δεν καταλαβαίνω γιατί δεν σου δούλεψε - άρεσε - βόλεψε - ικανοποίησε η λύση που πρότεινε ο Directx. Η STL είναι ένα πολύ δυνατό framework, το μόνο που θα ήθελε ο κώδικα του Directx είναι αντί για την main να δημιουργήσεις ένα general use function που θα δέχεται ως όρισμα το προς replace string και το string to replace, άντε και το όνομα του αρχείου.

Δημοσ.

Η λυση του Directx μια χαρα δουλευει, οι παραλαγες που εκανα σε αυτην δεν μου δουλεψαν.

Απλα ειπα οτι δεν θα δουλευε αν εψαχνες μονο για το balance. Θα επρεπε να ψαξεις για το username πρωτα (η οποια αλλη unique inforamation) και στην ιδια γραμμη με το username να αλλαξεις το balance.

Τελικα δουλεψε, βαζοντας το αρχειο σε ενα stringstream ανα γραμμη. χωριζοντας το stream σε stirngs ανα εγγραφη, αλλαζοντας την εγγραφη και ξαναποθηκευοντας ολο το αρχειο απο την αρχη με ios::trunc.

Αρχειοθετημένο

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

  • Δημιουργία νέου...