constructor does not work correctly while reading variables from txt file
The problem results from the wrong used algorithm and wrongly placed statements.
So, let's look what is going on in the below:
long linecount;
for(linecount=0;getline(pbin,line);linecount++)
;
contact* myArray = new contact[linecount];
pbin.seekg(0);
if(pbin.is_open()){
int i;
for(i=0;i<linecount;i++){
if(pbin!=NULL) {
while(pbin>>myArray[i]);
}
}
pbin.close();
You want to count the lines. So you read all lines until the eof
state is set. But, additionally, also the fail
bit will be set. See also here.
If you use your debugger, you will find a 3 in _Mystate
.
Then you perform a seekg
. This will reset the eof
bit but keep the fail
bit. The dubugger shows then
You can see that the fail
bit is still set.
So, and this will now lead to the main problem. If your write if(pbin!=NULL)
which is definitely wrong (on my machine is does not even compile), or if you better write if(pbin)
the fail bit will still be set. And because the bool
and the !
operator for streams is overwritten (please see here) the result of the if
and while
will be false and your pbin>>myArray[i]
will never be executed.
So, a pbin.clear()
would help.
But, although your class definition is already very good, with inserter and extractor overwritten, you do not use the full C++ power for reading the data.
One basic recommendation would be to never use raw pointers for owned memory. And best also not new
. Use dedicated containers for your purpose. E.g. a std::vector
. The you can use the std::vector
s constructor no 5 together with a std::istream_iterator
. Please read here. The range based constructor for the std::vector
will copy data from a given range, denoted by the begin and end iterator. And if you use the std::istream_iterator
, it will call your overwritten extractor operator, until all data are read.
So your main shrinks to:
int main() {
// Open source file and check, if it could be opened
if (ifstream pbin("r:\\phoneData2.txt");pbin) {
// Read complete source file
std::vector data(std::istream_iterator<contact>(pbin), {});
// Show data on console
std::copy(data.begin(), data.end(), std::ostream_iterator<contact>(std::cout, "\n"));
}
return 0;
}
This looks by far compacter and is easier to read. We start with an if-statement with initializer. The initializer parts defines the variable and the constructor will open the file for us. In the condition part, we simple write pbin
. And, as explained above, its bool
operator will be called, to check if everything was ok.
Please note:
- We do not need a
close
statement, because the destructor of thestd::ifstream
will close the file for us. - The outer namespace will not be polluted with the variable name
pbin
. That is one of the reasons, whyif
statement with initializer should be used.
We alread descibed the std::vector
with its range constructor. SO reading the complete file is simple done by the very simple statement
std::vector data(std::istream_iterator<contact>(pbin), {});
Please note:
- We do not define the type of the
std::vector
. This will be automatically deduced by the compiler through CTAD - We use the default initialzer
{}
for the end iterator, as can be seen here in constructor number 1.
The whole program could then be rewritten to:
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
class contact {
private:
int listno;
string name;
string surname;
string phonenumber;
public:
contact() {
this->name = "Unknown";
this->surname = "Unknown";
this->phonenumber = "Unknown";
this->listno = 0;
}
contact(string name, string surname, string phonenumber) {
this->name = name;
this->surname = surname;
this->phonenumber = phonenumber;
}
contact(int listno, string name, string surname, string phonenumber) {
this->name = name;
this->surname = surname;
this->listno = listno;
this->phonenumber = phonenumber;
}
friend ostream& operator<< (ostream& out, const contact& con) {
out << con.listno << '\t' << con.name << '\t' << con.surname << '\t' << con.phonenumber;
return out;
}
friend istream& operator>> (istream& in, contact& con) {
in >> con.listno >> con.name >> con.surname >> con.phonenumber;
return in;
}
};
int main() {
// Open source file and check, if it could be opened
if (ifstream pbin("r:\\phoneData2.txt");pbin) {
// Read complete source file
std::vector data(std::istream_iterator<contact>(pbin), {});
// Show data on console
std::copy(data.begin(), data.end(), std::ostream_iterator<contact>(std::cout, "\n"));
}
return 0;
}
How to read specific amount of character from file to struct
You're almost right. istream::getline()
waits for char *
at the first argument but you're are passing std::string
. Keep in mind if getline
doesn't read symbols to delimiter (by default, end of line and it is your case) it sets failbit for input stream (duom
). You need to istream::clear()
it. Also you're mixing duom >> n;
and file reading. Maybe you want to read info for structure from file and n
from cin
?
UPDATE
You need to skip a new line character with istream::ignore()
. Also your example has name with 20 symbols at 8 line but istream::getline
extracts one less than the specified. Following code doesn't contain any error checking.
int main()
{
ifstream duom("U2.txt");
competition athletes[30];
int n; // amount of athletes
duom >> n;
duom.ignore(1);
char name[256];
for (int i = 0; i < n; i++) {
duom.getline(name, 21);
if (duom.fail())
duom.clear();
athletes[i].name = name;
duom >> athletes[i].athleteNum >>
athletes[i].startHours >>
athletes[i].startMin >>
athletes[i].startSec;
duom.ignore(1);
}
return 0;
}
If you don't want to read other athlete members you should also skip them with istream::ignore()
instead of reading with duom >> ...
:
duom.ignore(32); // skip up to 32 characters or '\n'
https://en.cppreference.com/w/cpp/io/basic_istream/ignore
How to read in from a file word by word and assign those words to a struct? [duplicate]
In this piece of code
while (file >> file_string) {
b[count].title = file_string;
b[count].author = file_string;
count++;
}
you read one word and assign the same value to title and author, don't expect the compiler to guess your intentions ;)
Some additional hints and ideas:
while(!file.eof()
is not what you want, instead put the input operations into the loop condition. And you can skip the intermediate string and read directly into title
/author
:
void getBookData(book* b, int n, ifstream& file) {
int count = 0;
while((file >> b[count].title >> b[count].author) && count != n-1) {
count++;
}
}
Trouble reading in data from a file using data structures
As a general preliminary remark, I think that even for learning purpose, this kind of exercise should better let you use std::strings
instead of c-strings and std::vector
for keeping a growing number of items.
What's wrong in your code ?
The first problem is that you use the same counter count
to populate your agency array AND the car array. This will cause you very quickly to have a counter beyond the array boundaries and corrupt memory.
Solution: rework your loop structure using 2 distinct counters.
Next problem is that you don't identify the end of the car list of an agency. This makes it unrealistic to read more than one agency: you'll experience a failure on the stream reading that will prevent you getting anything usefull in your data.
Solution: analyze failures on reading to identify going from cars ( first element should be a number) to a new agency ( first element is a string).
In addition, you might have some strings which are longer than allowed by your character arrays, causing further memory corruption.
Solution: limit the number of chars read using iomanip() to fix maximum width. This is strongly recommended unless you go for std::string
Last issue: the variable length arrays are not a standard C++ feature, even if some popular compilers support it.
Solution: Either use dynamic allocation with new/delete or opt for the purpose of this excercise to use a constant maximum size.
Code snippet:
Adapted, without choices, menus, etc. , the reading would look like:
const int carAmount = 30; // !!!
const int agencyAmount = 10; // !!!
agency agencyLib[carAmount];
car carLib[carAmount];
ifstream carInData ("test.dat");
int numCar = 0, numAgency = 0; // !!! shows the real number of items available
int count1, count2; //
cout << "Start reading" << endl;
for (numAgency = numCar = 0; carInData && numAgency < agencyAmount; numAgency++) {
if (!(carInData >> setw(sizeof(agencyLib[numAgency].company)) >> agencyLib[numAgency].company >> agencyLib[numAgency].zip))
break; // if nothing left, exit loop immediately
for (; numCar < carAmount; numCar++) {
carInData >> carLib[numCar].year >> setw(sizeof(carLib[numCar].make )) >>carLib[numCar].make
>> setw(sizeof(carLib[numCar].model))>>carLib[numCar].model
>> carLib[numCar].price >> carLib[numCar].available;
if (carInData.fail()) { // here we expect a year, but get an agency string
carInData.clear();
break;
}
strcpy(carLib[numCar].agency, agencyLib[numAgency].company);
carLib[numCar].zip = agencyLib[numAgency].zip;
}
}
And the subsequent display:
cout << "Display agencies: " << endl;
for (count1 = 0; count1 < numAgency; count1++) {
cout << agencyLib[count1].company << " " << agencyLib[count1].zip << "\n";
}
cout << "Cars: " << endl;
for (count2 = 0; count2 < numCar; count2++) {
cout << carLib[count2].agency << " " << carLib[count2].zip << ": ";
cout << carLib[count2].year << " " << carLib[count2].make << " " << carLib[count2].model << " " << carLib[count2].price << " " << "\n";
}
Note that there's no link beteween agencies and cars (except the common fields), so the display just shows two distinct lists.
Improving error processing for an istream helper class when using exceptions
I was so concentrated on searching std functions to solve that, that I didn't think of the most obvious solution: hijacking the std exception thrown. In case it could help s.o. else:
I first defined a dedicated nested failure class:
class mandatory_input {
public:
...
class failure : public std::istream::failure {
public:
failure(std::error_code e);
};
};
Then I have added the following bloc in the original error processing code (see question):
// start of enhanced exception handling
if (is.exceptions() & std::istream::failbit) { // if exception will be thrown
try {
is.setstate(std::ios::failbit);
} catch (std::istream::failure &e) { // the failbit will trigger this
throw mandatory_input::failure(e.code()); // and i throw my own
} catch (std::exception &e) { // just in case other low-level errors would be thrown
throw e;
}
} else //======= end of enhanced exceptions handling
Now with this solution, the clients of my helper class who want to use .exceptions()
can process errors either undifferentiated:
try { cin >> mandatory_input(" ( + ") >> country >> .... ;
} catch (istream::failure e) {
cerr << "Input error: "<< e.code()<< " " << e.what();
}
or fine tune error processing:
try { ....
} catch (mandatory_input::failure e) {
cerr << "Input format mismatch: " << mandatory_input::getexpected()
<< " was expected, but " << mandatory_input::getread_error() << " was read !\n";
} catch (istream::failure e) {
cerr << "Input error: "<< e.code()<< " " << e.what();
}
How to iterate over a line in a file? [duplicate]
You're almost there; you can use formatted stream extraction to read integers, using a string stream to represent each line:
#include <fstream>
#include <sstream>
#include <string>
// ...
for (std::string line; std::getline(infile, line); )
{
std::istringstream iss(line);
for (int n; iss >> n; )
{
std::cout << "Have number: " << n << "\n";
}
std::cout << "End of line\n";
}
Error checking can be added by checking whether the entire string stream has been consumed.
Incorrect char array length when using reinterpret_cast to store data with struct object [duplicate]
As your char array is not nul terminated, you have to display each character instead of the (decayed) const char*
:
template <std::size_t N>
void print(const char (&a)[N])
{
for (auto c : a)
std::cout << c;
}
and then
Student student;
// init student
// ...
print(student.f_name); // instead of std::cout << student.f_name;
print(student.l_name); // instead of std::cout << student.l_name;
std::cout << student.camp_code;
// ...
Related Topics
Count How Many Times Elements in an Array Are Repeated
Given an Integer N. What Is the Smallest Integer Greater Than N That Only Has 0 or 1 as Its Digits
How to Install (V142) Build Tools in Visual Studio
Forcing the Qt Gui to Update Before Entering a Separate Function
What Is This Weird Colon-Member (": ") Syntax in the Constructor
What Are Rvalues, Lvalues, Xvalues, Glvalues, and Prvalues
Why Should I Prefer to Use Member Initialization Lists
Definitive List of Common Reasons For Segmentation Faults
Best Practices For Circular Shift (Rotate) Operations in C++
What Are the Evaluation Order Guarantees Introduced by C++17
How to Serialize and Deserialize a Class in C++
What Will Happen When I Call a Member Function on a Null Object Pointer
Function With Same Name But Different Signature in Derived Class
Why C++11 In-Class Initializer Cannot Use Parentheses
How to Expand a Tuple into Variadic Template Function'S Arguments