C++/Binary File
Expert: Zlatko - 1/17/2010
QuestionQUESTION: I need to perform a few tasks:
1. Read the infile.txt
2. Creation of binary file
3. Update binary file
4. Append some records to binary file
Attached is the code for task 1. Somehow, it can compile & build but execute will crash the compiler. Pls advise whether my task 1 is correct?
[Code]
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MAX = 20;
const int MAX1 = 30;
struct UKStudent
{
char name [MAX1];
int noTest;
float mark [MAX];
int final;
char grade [MAX1];
};
struct OverseasStudent
{
char country [MAX1];
char name [MAX1];
int noTest;
float mark [MAX];
int final;
char grade [MAX1];
};
union EveryStudent
{
UKStudent uks;
OverseasStudent oss;
};
struct Student
{
char type;
EveryStudent st;
};
int arraytofile (fstream&, char [], Student []);
int main()
{
fstream afile;
Student stu [MAX];
int size = arraytofile (afile,"infile.txt",stu);
}
int arraytofile (fstream& afile, char filename [], Student stu [])
{
afile.open (filename, ios::in);
if(!afile.good ())
{
cout << "The input file does not exist. " << endl;
return 1;
}
char n;
int i = 0;
while (afile >> n)
{
switch (n)
{
case 'U': stu [i].type; //Correct? How to refer back to infile.txt, first column?
afile >> stu [i].st.uks.name [MAX1];
break;
case 'O': stu [i].type; //Correct? How to refer back to infile.txt, first column?
afile >> stu [i].st.oss.country [MAX1];
afile >> stu [i].st.oss.name [MAX1];
}
++i;
}
afile.close ();
return i;
}
________________________________________________________
infile.txt
-----------------------------------------------------------------
U David
O American Michael_Clinton
O Malaysian Mohd_Ali
----------------------------------------------------------------------
ANSWER: Hello Steven
The corrections to you Task 1 are below.See my comments starting with //XXX
The problem is that when reading into an array, you cannot specify the array length. Unfortunately that makes reading text into an array a bit unsafe. I prefer reading into a string.
afile >> stu [i].st.uks.name[MAX]
should read
afile >> stu [i].st.uks.name
The code is below.
Best regards
Zlatko
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MAX = 20;
const int MAX1 = 30;
struct UKStudent
{
char name [MAX1];
int noTest;
float mark [MAX];
int final;
char grade [MAX1];
};
struct OverseasStudent
{
char country [MAX1];
char name [MAX1];
int noTest;
float mark [MAX];
int final;
char grade [MAX1];
};
union EveryStudent
{
UKStudent uks;
OverseasStudent oss;
};
struct Student
{
char type;
EveryStudent st;
};
int arraytofile (fstream&, char [], Student []);
int main()
{
fstream afile;
Student stu [MAX];
int size = arraytofile (afile,"infile.txt",stu);
}
int arraytofile (fstream& afile, char filename [], Student stu [])
{
afile.open (filename, ios::in);
if(!afile.good ())
{
cout << "The input file does not exist. " << endl;
return 1;
}
char n;
int i = 0;
while (afile >> n)
{
switch (n)
{
case 'U':
stu [i].type = 'U'; //XXX save the first column
afile >> stu [i].st.uks.name; //XXX remove [MAX1];
break;
case 'O':
stu [i].type = 'O'; //XXX save the first column
afile >> stu [i].st.oss.country; //XXX remove [MAX1];
afile >> stu [i].st.oss.name;//XXX remove [MAX1];
}
++i;
}
afile.close ();
return i;
}
---------- FOLLOW-UP ----------
QUESTION: Tks. But I amended my code.
Next, I am going write a function to open the binary file, outfile.dat
and update the following data to a text file, update.txt
I am lost on how to write the data to update.txt in void process_text function
[update.txt]
----------------------------------------------------------------
1 UK Larry_Bird 0 0 0 0 0 Fail
2 Aust Huck_Hogan 0 0 0 0 0 Fail
3 Indian Suresh 0 0 0 0 0 Fail
--------------------------------------------------------------
0 - noTest.mark
Fail - grade
[Code]
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MAX = 20;
const int MAX1 = 30;
struct UKStudent
{
char name [MAX1];
int noTest;
float mark [MAX];
int final;
char grade [MAX1];
};
struct OverseasStudent
{
char country [MAX1];
char name [MAX1];
int noTest;
float mark [MAX];
int final;
char grade [MAX1];
};
union EveryStudent
{
UKStudent uks;
OverseasStudent oss;
};
struct Student
{
char type;
EveryStudent st;
};
int arraytofile (fstream&, char [], Student []);
void process_bintext (fstream&, char [], Student [],int);
void process_text (fstream&, char [], Student [], int);
int main()
{
fstream afile;
Student stu [MAX];
int size = arraytofile (afile,"infile.txt",stu);
process_bintext (afile,"outfile.dat", stu, size);
process_text (afile,"update.txt", stu, size);
}
//Read infile.txt
int arraytofile (fstream& afile, char filename [], Student stu [])
{
afile.open (filename, ios::in);
if(!afile.good ())
{
cout << "The input file does not exist. " << endl;
return 1;
}
char n;
int i = 0;
while (afile >> n)
{
switch (n)
{
case 'U': stu [i].type = n;
afile.getline (stu [i].st.uks.name,MAX);
break;
case 'O': stu [i].type = n;
afile.getline (stu [i].st.oss.country,MAX,' ');
afile.getline (stu [i].st.oss.name,MAX);
break;
}
++i;
}
afile.close ();
return i;
}
//Convert infile.txt to binary file
void process_bintext (fstream& afile, char filename [], Student stu[], int size)
{
afile.open (filename, ios::out | ios::binary);
if(!afile.good())
{
cout <<"Failed to write file. " << endl;
return;
}
char n;
for (int i = 0; i < size; i++)
{
if (stu [i].type == n)
{
afile.write (&stu[i].type,1);
afile.write (stu [i].st.uks.name,MAX);
}
else
{
afile.write(&stu[i].type,1);
afile.write (stu [i].st.oss.country,MAX);
afile.write (stu [i].st.oss.name,MAX);
}
}
afile.close ();
}
// I have problem with this function, unsure how to update
//Process binary file to update text file
void process_text (fstream& afile, char filename [], Student stu[], int size)
{
(filename, ios::in | ios::out | ios::binary);
if(!afile.good ())
{
cout << "Failed to update." << endl;
return;
}
int i;
int k;
//Student st;
///afile.seekg (k-1) * sizeof (Student), ios::beg);
afile.read (&stu[i].type,1);
afile.read (stu [i].st.uks.name,MAX);
afile.read (stu [i].st.oss.country,MAX);
afile.read (stu [i].st.oss.name,MAX);
for (int i = 0; i < size; i++)
{
//afile.write (&stu[i].type,1);
afile << i + 1 << endl;
afile.write (stu [i].st.oss.country,MAX);
//afile << (stu [i].st.uks.name || stu [i].st.oss.name) << endl;
//afile << (stu [i].st.uks.noTEST || stu [i].st.oss.noTEST) << endl;
}
//afile.seekp (k-1) * sizeof (Student), ios::beg);
afile.close ();
}
AnswerSteven, I find your question very confusing, but I have tried my best to make sense out of it. I have corrected your creation of the binary file and I have started the function to create update.txt from the binary file. I leave it to you to finish. My comments and instructions are in the code and start with XXX. Have you changed the format of infile.txt ? The code you sent me does not read infile.txt in this format:
________________________________________________________
infile.txt
-----------------------------------------------------------------
U David
O American Michael_Clinton
O Malaysian Mohd_Ali
----------------------------------------------------------------------
I have changed to code back to what you had earlier and disabled your input code using #if 1 / #else /#endif
Feel free to change it back, but do let me know the new format of infile.txt.
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MAX = 20;
const int MAX1 = 30;
struct UKStudent
{
char name [MAX1];
int noTest;
float mark [MAX];
int final;
char grade [MAX1];
};
struct OverseasStudent
{
char country [MAX1];
char name [MAX1];
int noTest;
float mark [MAX];
int final;
char grade [MAX1];
};
union EveryStudent
{
UKStudent uks;
OverseasStudent oss;
};
struct Student
{
char type;
EveryStudent st;
};
int arraytofile (fstream&, char [], Student []);
void process_bintext (fstream&, char [], Student [],int);
//XXX changed process_text prototype
/* XXX I'm not sure what you need to do here, but I understand that you need to
read from the binary file and write to some text file. You cannot use one stream
for both files, so I changed the function to accept an input file name and an output
file name.
*/
void process_text (char in_binFilename[], char out_txtFilename [], Student stu[], int size);
int main()
{
/*XXX You really should not have fstream afile here. There is no need to pass it to each function.
You should pass only the file name, and have a local afile in each function. Then, when each function
exits, the afile destructor will close the fstream automatically.
If you have an afile object passed around to all the functions, then error bits set by one function will
be seen by the next function unless you remember to call afile.clear()
*/
fstream afile;
Student stu [MAX];
//XXX it is important to initialize your students otherwise the memory will be filled
//XXX with garbage. Either use a constructor in Student, or set memory to 0 here.
memset(stu, 0, sizeof(stu)); //XXX initialize students array
int size = arraytofile (afile,"infile.txt",stu);
process_bintext (afile,"outfile.dat", stu, size);
process_text ("outfile.dat", "update.txt", stu, size);
}
//Read infile.txt
//XXX This should be called filetoarray, not array to file
int arraytofile (fstream& afile, char filename [], Student stu [])
{
afile.open (filename, ios::in);
if(!afile.good ())
{
cout << "The input file does not exist. " << endl;
return 1;
}
char n;
int i = 0;
while (afile >> n)
{
switch (n)
{
#if 1 //XXX This works with the infile.txt format you specified in the first message.
case 'U':
stu [i].type = 'U';
afile >> stu [i].st.uks.name;
break;
case 'O':
stu [i].type = 'O';
afile >> stu [i].st.oss.country;
afile >> stu [i].st.oss.name;
#else
//XXX this is not right, if you are still using the infile.txt
//XXX format from your first message to me.
/*
infile.txt
-----------------------------------------------------------------
U David
O American Michael_Clinton
O Malaysian Mohd_Ali
----------------------------------------------------------------------
*/
//XXX Have you changed it to have name and country on separate lines?
case 'U': stu [i].type = n;
afile.getline (stu [i].st.uks.name,MAX);
break;
case 'O': stu [i].type = n;
afile.getline (stu [i].st.oss.country,MAX,' ');
afile.getline (stu [i].st.oss.name,MAX);
break;
#endif
}
++i;
}
afile.close ();
return i;
}
//Convert infile.txt to binary file
void process_bintext (fstream& afile, char filename [], Student stu[], int size)
{
/*XXX Other functions may have left afile in a bad state,
so if you intend to pass afile around instead of making it local to the
function, you should close and clear afile before opening. It is not elegant
to have this function cleanup after other functions. I leave it to you
to remove afile from the function parameters.
*/
afile.close();
afile.clear();
afile.open (filename, ios::out | ios::binary | ios::trunc); //XXX added ios::trunc
if(!afile.good())
{
cout <<"Failed to write file. " << endl;
return;
}
//XXX you do not need to write individual fields into the binary file.
//XXX you can simply dump the memory of each array element, in a loop,
//XXX or you can just dump the entire array.
//XXX It is a binary file so don't expect to be able to read it with a text editor.
//XXX do this: afile.write((char*)stu, size * sizeof(Student));
//XXX or do this
for(int ix = 0; ix < size; ++ix)
{
afile.write((char*)&stu[ix], sizeof(Student));
}
afile.close ();
}
// I have problem with this function, unsure how to update
//Process binary file to update text file
void process_text (char in_binFilename[], char out_txtFilename [], Student stu[], int size)
{
/* XXX I'm not sure what you need to do here, but I understand that you need to
read from the binary file and write to some text file. You cannot use one stream
for both files, so I changed the function to accept an input file name and an output
file name.
*/
fstream fin(in_binFilename, ios::binary | ios::in);
if (!fin.good())
{
cout <<"Failed to open input file. " << endl;
return;
}
fstream fout(out_txtFilename, ios::out);
if (!fout.good())
{
cout << "Failed to ipen output file. " << endl;
return;
}
Student student;
while(true)
{
// Read 1 student from binary file
fin.read((char*)&student, sizeof(Student));
if (fin.eof()) break;
/*XXX I leave it to you to complete writing the text file.
I believe you want this format:
1 UK Larry_Bird 0 0 0 0 0 Fail
2 Aust Huck_Hogan 0 0 0 0 0 Fail
3 Indian Suresh 0 0 0 0 0 Fail
*/
}
}