c++ reading a file into a struct and writing a binary file - c++

I have a text file that contains over 5000 lines with data (lottery draw results for Lotto). Each line has the form: number. day.month.year number1,number2,number3,number4,number5,number6
Five sample lines:
27.01.1957 8,12,31,39,43,45
03.02.1957 5,10,11,22,25,27
10.02.1957 18,19,20,26,45,49
17.02.1957 2,11,14,37,40,45
24.02.1957 8,10,15,35,39,49
I have also:
struct Lotto
{
short number_drawing;
char day;
char month;
short year;
char tab[6];
};
I have to write data from this text file into a binary file as struct Lotto.
I have already run out of ideas.
I have beeng trying since few days but my program still doesn't work properly :(
I try to load although one line :)
int main()
{
ifstream text("lotto.txt", ios::in);
ofstream bin("lottoBin.txt", ios::binary | ios::out);
Lotto zm;
short number_drawing;
char day;
char month;
short year;
char tab[6];
char ch;
int records = 0;
while (!text.eof())
{
text >> zm.number_drawing >> ch >> zm.day >> ch >> zm.month >>
ch >> zm.year >> zm.tab[0] >> ch >> zm.tab[1] >> ch >> zm.tab[2] >>
ch >> zm.tab[3] >> ch >> zm.tab[4] >> ch >> zm.tab[5];
records++;
}
cout << "All records: " << records << endl;

Here are some observations that might help you:
You will not be able to directly read a number into a char. Use an intermediate integer.
Define a function to read a record: bool read( std::istream&, Lotto& )
Your while should call the above function: while ( read( is, lotto ) )
A starting point:
bool read( std::istream& is, Lotto& lotto )
{
short t;
char c;
// read data
//
is >> t;
lotto.number_drawing = t;
is >> c;
if ( c != '.' )
return false;
//
is >> t;
lotto.day = char( t );
is >> c;
if ( c != '.' )
return false;
// read rest of fields...
// end of line
while ( is.get( c ) && isspace( c ) && c != '\n' )
;
if ( ! is.eof() && c != '\n' )
return false;
// check data
if ( lotto.month > 12 )
return false;
// check rest of data...
return is.good();
}
int main()
{
ifstream is( "yourfile.txt" );
if ( ! is )
return -1;
Lotto lotto;
while ( read( is, lotto ) )
{
// ...
}
if ( !is.eof() )
return -1;
return 0;
}

Related

while Infinite loop in function

bool isDigital(char c) { return ('0' <= c && c <= '9'); }
void DigitalToken( char digitToken[50], char ch ) {
digitToken[0] = ch;
char input = '\0';
cin >> input;
int i = 0;
while ( ( input != ' ' ) && ( input != '\t' ) && ( input != '\n' ) ) { // got a infinite loop
i++;
digitToken[i] = input;
cin >> input;
} // while ( input != ' ' && input != '\t' && input != '\n' )
} // DigitalToken()
int main() {
char ch = '\0';
while ( cin >> ch ) {
if ( isDigital(ch) ) {
char* digitToken = new char[50]();
DigitalToken(digitToken, ch);
cout << digitToken;
delete[] digitToken;
} // else if
} // while
} // main()
I don't understand why I got a infinite loop in DitgitalToken function.
When I input 123, it should be output 123.
I watched it for a long time, but I still don’t know why and how to fix it.
Instead of cin >> input; use input = cin.get();.
You have to be careful while using cin with characters or strings. It treats spaces, tabs, newlines as end of the input and hence do not treat them as input themselves.
Your program blocks in the while loop on cin >> input; after it read "123". As #Arty suggested use cin.get() instead as whitespace is stripped by default. You can also use cin >> noskipws; prior to executing cin >> input;. See skipws.

C++ Reading text file into struct data members

I'm currently trying to load a text file into struct data members. Each number is separated by a comma.
#include<string>
#include<sstream>
using namespace std;
struct server{
bool isBusy;
};
struct pass{
double arrivalTime = 0;
double serviceTime = 0;
int classType = 0;
};
int main(){
string fileName;
string line;
pass Pass;
cout << "Enter file name: ";
cin >> fileName;
ifstream fin(fileName);
while (getline(fin, line, ','))
{
/*NEED HELP HERE*/
fin >> Pass[0].arrivalTime;
fin >> Pass[0].serviceTime;
fin >> Pass[0].classType;
}
}
Here is an example of the text file.
0.951412936,2.131445423,0
1.902743503,2.010703852,0
2.537819984,2.326199911,0
3.425838997,1.603712153,0
3.502553324,0.998192867,0
3.917348666,1.49223429,0
4.391605986,0.831661367,0
4.947059678,0.8557003,0
5.429305232,2.42029408,0
The data in the text file follows this format:
arrivalTime,serviceTime,classType
As you can see i have split the line up and stored it in "line" using the comma delimiter, but i am unsure how to load each number into the struct in the while loop.
Any help would be appreciated.
Define an istream operator >> for your struct. Something like
struct pass {
double arrivalTime = 0;
double serviceTime = 0;
int classType = 0;
friend std::istream & operator >>(std::istream & in, pass & p) {
char c;
in >> p.arrivalTime >> c >> p.serviceTime >> c >> p.classType;
return in;
}
};
Then, simply
pass Pass;
fin >> Pass;
while (getline(fin, line))
{
sscanf(line.c_str(), "%lf,%lf,%d", &arrivalTime, &serviceTime, &classType);
}
This loop is wrong:
while (getline(fin, line, ','))
{
/*NEED HELP HERE*/
fin >> Pass[0].arrivalTime;
fin >> Pass[0].serviceTime;
fin >> Pass[0].classType;
}
You are reading everything from the stream up to the next ',' character, then trying to read more from the stream.
Given the input file:
0.951412936,2.131445423,0
1.902743503,2.010703852,0
2.537819984,2.326199911,0
Your program reads "0.951412936" into line (and discards the ',') then tries to read the next input into Pass[0].arrivalTime but the next input is 2.131445423, which was meant to be the serviceTime (which you already read into line).
As Shreevardhan suggests you can define an operator for reading your struct from a stream. I would make it more reliable like so:
struct ExpectedChar { char expected; };
// read a character from a stream and check it has the expected value
std::istream& operator>>(std::istream& in, const ExpectedChar& e)
{
char c;
if (in >> c)
if (c != e.expected) // failed to read expected character
in.setstate(std::ios::failbit);
return in;
}
// read a struct pass from a stream
std::istream& operator>>(std::istream& in, pass& p)
{
ExpectedChar comma{ ',' };
in >> p.arrivalTime >> comma >> p.serviceTime >> comma >> p.classType;
return in;
}
This will stop reading if the input file does not meet the expected format. Now you can do:
while (fin >> Pass)
{
// do something with each pass
}
if (!fin.eof()) // stopped reading before end-of-file
throw std::runtime_error("Invalid data in input file");
This will keep reading a pass from the file until reading fails, either because it reached the end of the file, or because there was some bad data in the file. If there is bad data it throws an exception.
#include<string>
#include<iostream>
#include<fstream>
#include<vector>
using namespace std;
struct server{
bool isBusy;
};
struct pass{
double arrivalTime;
double serviceTime;
int classType;
friend std::istream & operator >>(std::istream &in, pass &p) {
char c;
in >> p.arrivalTime >> c >> p.serviceTime >> c >> p.classType;
return in;
}
};
int main(){
string fileName;
string line;
cout << "Enter file name: ";
cin >> fileName;
ifstream fin(fileName.c_str(), ifstream::in);
vector<pass> passes;
pass Pass;
while (fin>>Pass)
passes.push_back(Pass);
for(vector<pass>::const_iterator iter = passes.begin();
iter != passes.end();
++iter)
std::cout<<iter->arrivalTime<<" "<<iter->serviceTime<<" "
<<iter->classType<<std::endl;
}
Here is hint;
string line;
string temp;
string::size_type sz;
while (getline(cin, line))
{
istringstream ss( line );
getline( ss, temp, ',' );
double arrivalTime = stod(temp, &sz);
getline( ss, temp, ',' );
double serviceTime = stod(temp, &sz);
getline( ss, temp, ',' );
double classType = stod(temp, &sz);
cout << arrivalTime << ' '
<< serviceTime << ' '
<< classType << endl;
}

Reading from file in C++, dealing with blanks

I am tying to read from a text file in C++98. It has a pattern, but sometimes a field is empty:
ID Name Grade level
1 a 80 A
2 b B
3 c 90 A
How can I read from file such that I can ignore the blanks?
( I wish I could simply use Regex: \d*)
Is there any simple way of doing that?
You need to use what knowledge you have about the input to make assumptions about what is missing. You can use std::stringstream to parse individual terms from a text line. In other words std::stringstream deals with blanks by ignoring spaces and getting a complete term only, for example std::stringstream("aaa bbb") >> a >> b will load strings a with "aaa" and b with "bbb".
Here is an example program that parses the input, making a robust parser from scratch can be difficult, but if your input is strict and you know exactly what to expect then you can get away with some simple code:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
//-----------------------------------------------------------------------------
// holds a data entry
struct Entry {
int id;
std::string name;
int grade;
std::string level;
Entry() {
// default values, if they are missing.
id = 0;
name = "Unknown";
grade = 0;
level = "?";
}
void ParseFromStream( std::stringstream &line ) {
std::string s;
line >> s;
if( s[0] >= '0' && s[0] <= '9' ) {
// a number, this is the ID.
id = atoi( s.c_str() );
// get next term
if( line.eof() ) return;
line >> s;
}
if( s[0] >= 'a' && s[0] <= 'z' || s[0] >= 'A' && s[0] <= 'Z' ) {
// a letter, this is the name
name = s;
// get next term
if( line.eof() ) return;
line >> s;
}
if( s[0] >= '0' && s[0] <= '9' ) {
// a number, this is the grade
grade = atoi( s.c_str() );
// get next term
if( line.eof() ) return;
line >> s;
}
// last term, must be level
level = s;
}
};
//-----------------------------------------------------------------------------
int main(void)
{
std::ifstream input( "test.txt" );
std::string line;
std::getline( input, line ); // (ignore text header)
while( !input.eof() ) {
Entry entry;
std::getline( input, line ); // skip header
if( line == "" ) continue; // skip empty lines.
entry.ParseFromStream( std::stringstream( line ));
std::cout << entry.id << ' ' << entry.name << ' ' <<
entry.grade << ' ' << entry.level << std::endl;
}
return 0;
}

C++ read file, search for hex, output file

I can't figure out why this is outputting a different file given that the start of the input file matches the first if statement. Also, if there are multiple finds, the next file name turns out to be the last file name plus the new file name together.
int main()
{
unsigned short n(0);
unsigned char y;
std::ostringstream filename;
std::ifstream input("data.dat", std::ios::binary);
input >> std::noskipws >> std::hex;
std::ofstream output;
output << std::ios::binary;
while (input.good()) {
filename.clear();
filename << n << ".dat";
output.open(filename.str());
while ( input.good() )
{
input >> y;
output << y;
if ( y == 0xFF ) {
input >> y;
output << y;
if ( y == 0xD9 ) {
input >> y;
output << y;
output.close();
break;
}
}
}
if (output.is_open())
output.close();
++n;
}
return 0;
}
#include <iostream> // for input/output
#include <fstream> // for file stream stuff
#include <sstream> // for ostringstream
int main()
{
unsigned short nFile = 0; // used to increment file name
std::ostringstream filename; // output filename
std::ifstream inFile; // input stream
std::ofstream outFile; // output stream
unsigned char buffer; // buffer between input and output
inFile.open("data.dat", std::ios::binary); // open input file
if ( inFile.fail() )
return 1;
while ( inFile.good() )
{
filename.str(""); // empties the stream
filename << nFile << ".dat"; // generate filename
// start the search loop
while ( inFile.good() )
{
// create a file
outFile.open(filename.str(), std::ios::binary);
if ( outFile.fail() )
return 1;
while ( inFile.good() )
{
inFile.read(reinterpret_cast<char*>(&buffer), sizeof(buffer)); // read a byte
if ( inFile.eof() )
break;
outFile.write(reinterpret_cast<char*>(&buffer), sizeof(buffer)); // write a byte
if ( buffer == 0xFF )
{
inFile.read(reinterpret_cast<char*>(&buffer), sizeof(buffer)); // read a byte
if ( inFile.eof() )
break;
outFile.write(reinterpret_cast<char*>(&buffer), sizeof(buffer)); // write a byte
if ( buffer == 0xD9 ) // full match; break loop
{
break;
}
}
}
// if search is done and file is still open, close it
if (outFile.is_open())
outFile.close();
++nFile;
}
return 0;
}
}

What is the cin analougus of scanf formatted input?

With scanf there's, usually, a direct way to take formatted input:
1) line with a real number higher than 0, and less than 1. Ending on 'x', e.g: 0.32432523x
scanf("0.%[0-9]x", &number);
2) line represents an addition in the format :30+28=fifty-eight
scanf(":%d+%d=%99s", &number1, &number2, &total);
What is the cin solution, using only the standard library?
You can make a simple class to verify input.
struct confirm_input {
char const *str;
confirm_input( char const *in ) : str( in ) {}
friend std::istream &operator >>
( std::istream &s, confirm_input const &o ) {
for ( char const *p = o.str; * p; ++ p ) {
if ( std::isspace( * p ) ) {
std::istream::sentry k( s ); // discard whitespace
} else if ( (c = s.get() ) != * p ) {
s.setstate( std::ios::failbit ); // stop extracting
}
}
return s;
}
};
usage:
std::cin >> x >> confirm_input( " = " ) >> y;
Use the >> operator to read from cin.
int number1, number2;
std::string text;
char plus, equals;
std::cin >> number1 >> plus >> number2 >> equals >> text;
if (!std::cin.fail() && plus == '+' && equals == '=' && !text.empty())
std::cout << "matched";
It's not as good as scanf because you'd have to verify any literals that were in the scanf string yourself. Doing it with streams will almost certainly be a lot more lines of code than scanf.
I would use scanf.