Why won't this file read in successfully? - c++

I was trying to read in a .dat file that looks like this:
T001CD1 10000.00 2.5 2
T001CD2 50000.00 3.5 6
H407CD1 20000.00 2.0 1
M555CD1 30000.00 3.5 5
N423CD1 50000.00 3.0 4
N423CD2 60000.00 2.5 2
S602CD1 80000.00 4.0 8
H707CD1 25000.00 2.5 7
With this Code:
void readCdAccountInfo()
{
ifstream in_stream;
in_stream.open("CdAccounts.dat");
while (!in_stream.eof())
{
int i = 0;
string iDTemp;
float ratetemp;
int yeartemp;
double depotemp;
while (in_stream
>> iDTemp
>> depotemp
>> ratetemp
>> yeartemp
)
{
CCdAccount temp(iDTemp, depotemp, ratetemp, yeartemp);
accounts[i] = temp;
i++;
}
{
if (in_stream.fail())
{
cout << "Input file opening failed. \n";
exit(1);
}
in_stream.close();
}}
}
ID, Deposit (private member), Rate, and Year are are all part of a class I have.
The only thing that pops up when I run my main is the input file failure message.

First, check for opening error immediately after opening the file.
ifstream in_stream("CdAccounts.dat");
if (in_stream.fail())
{
cout << "Input file opening failed. \n";
exit(1);
}
The outer loop while (!in_stream.eof()) is obsolete, as you already check for stream errors in the inner loop correctly.
In the following code, I don't know the type of variable accounts, but it appears to be an array. It's not directly related to the issues you experience but that array may overflow if the file contains more records than you have reserved for your array. I suggest using std::vector instead, which will resize automatically.
int i = 0;
string iDTemp;
float ratetemp;
int yeartemp;
double depotemp;
while (in_stream
>> iDTemp
>> depotemp
>> ratetemp
>> yeartemp
)
{
CCdAccount temp(iDTemp, depotemp, ratetemp, yeartemp);
// Instead of this...
accounts[i] = temp;
i++;
// ... I suggest declaring accounts as std::vector<CCdAccount>
// so you could add elements like this:
// accounts.push_back( temp );
}
Next issue is in the error checking after this code. As commenter Peter pointed out:
Reaching end of file sets the eofbit for a stream. However, operations
that attempt to read something (like operator>>()) also set the
failbit if reaching end of file causes them to fail (i.e. to not
receive input).
So to check only for unexpected read errors, we have to exclude the eof bit from the error condition:
if (in_stream.fail() && ! in_stream.eof())
{
cout << "Input file reading failed.\n";
exit(1);
}
At the end of the function there is no need to explicitly close() the stream as the destructor of the stream will automatically close the file when the scope ends. It's not an error to call close(), it's just obsolete.

Related

Editing a text file if searched value is present

I have just a couple issues here with my code. It works but I'm not advanced enough to do what I want to yet. Nor do I know how to word it for a google search. I have a Blackjack game that I'm doing and want to know how to edit certain lines of the file depending on user input. Simpler, I want a user to be able to open the game and start with their balance from the last time they were playing(a save and load feature). My issues are the balance and the username are on the same line in the text file (purposefully) and I want to assign the variables to those in the text file. I know I'm on the right track, I just dont know where to go from here. Thanks for the help in advance. If I broke a rule of posting, I'm sorry.
input username
if username is present in file
edit balance on leave
if username isnt present in file
create new user
Here is my code for the load function:
void load(userAcc user1)
{
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
string word;
for (int x = 0; in >> word; x++);
{
user1.name = word;
user1.balance = word;
}
cout << user1.name << endl;
cout << user1.balance << endl;
in.close();
}
else
cout << "Cannot open a file";
}
void save(userAcc user1)
{
user1.balance = "1000";
cout << "Enter a username: ";
cin >> user1.name;
ofstream out;
out.open("Balances.txt", ios_base::app);
if (out.is_open())
{
out << user1.name << " " << user1.balance << endl;
out.close();
}
else
cout << "Cannot open a file";
}
In
for (int x = 0; in >> word; x++);
remove the trailing ;. It ends the statement before the body of the for loop, separating the two. The for spins around doing nothing but reading the file until it ends and incrementing the unused variable x and the following code block will be run exactly once, storing whatever is in word (and since the loop will exit when the read into word fails, what's in word will depend on the C++ Standard version the the compiler's been set to use) into user1.
Once the ; is removed, the for loop will read into word until no more words can be read from the file. Every word read is copied into the same userAcc writing over the previous word. When the file hits the end in >> word will fail and the loop will exit. The last word in the file will then be printed out, all other words having been overwritten.
Naïve fixing of this would look something like
void load(userAcc user1)
{
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
while (in >> user1.name // read name in from file
>> user1.balance) // read balance in from file
{ // loop will exit when it cannot read a name and a balance from the file
// for now we're just printing out what's read from the file.
cout << user1.name << endl << user1.balance << endl;
}
// in.close(); not needed. File will automatically close when in goes out of scope.
}
else
cout << "Cannot open a file";
}
But we probably want to do more than print out all of the users in the file, so let's put them into a convenient resizable container like std::vector.
vector<userAcc> load() // takes no parameters, returns list of accounts
{
vector<userAcc> accounts;
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
userAcc user1; // account we can read into
while (in >> user1.name >> user1.balance)
{
accounts.push_back(user1); // store account
}
}
else
cout << "Cannot open a file";
return accounts; // hand accounts read back to caller.
}
Use of the function would be something like
vector<userAcc> accounts = load();
The save function looks pretty much good-to-go as written.

reading a file with a running name from 0 to N

I have N+1 files in a folder called b0.txt,b1.txt,b2.txt, ....,bN.txt.
I would like to open them inside a loop because for each of them I would like to copy the first 15 characters inside an array.
The code lines I wrote are basically:
int main(){
int N=4;
int i;
char number [15];
for(i=0; i< N; i++){
ifstream OpenFile("b%i.txt");
int l=0;
while(!OpenFile.eof()) {
OpenFile >> number [l];
l++;
}
OpenFile.close();
}
}
I'm using Dev C++ and when I compile these code lines no errors are shown. However, I'm not able to run the program.
Do you have any tip?
You should build the string name of the file. You might try:
char bufname[64];
snprintf(bufname, sizeof(bufname), b%i.txt", i);
ifstream OpenFile(bufname);
or use std::string or std::ostringstream tricks.
The filename "b%i.txt" is used explicitly as written, not as a printf-style format specifier.
You can either use sprintf, e.g.:
char filename[512];
sprintf(filename, "b%i.txt", i);
ifstream OpenFile(filename);
or use the C++ ostringstream, e.g.:
std::ostringstream filename;
filename << "b" << i << ".txt";
ifstream OpenFile(filename.str().c_str());
"b%i.txt" doesn't put i into the string... you can use:
std::ostringstream oss;
oss << 'b' << i << ".txt";
if (std::ifstream f(oss.str().c_str()))
...f is an open stream - use it here...
else
std::cerr << "couldn't open file " << oss.str() << '\n';
Don't test for eof - it doesn't work like that. Just use read and gcount:
if (OpenFile.read(number, sizeof number) && f.gcount() == sizeof number)
...you got the data...
else
std::cerr << "unable to read 15 characters from " << oss.str() << '\n';
(FWIW, eof is set after an input operation is concluded or aborted due to hitting eof, so it's not false before you attempt input, and it not being set doesn't guarantee the next operation will succeed - how could it if the stream doesn't yet know what you'll try to read?)
Your program does nothing to get the value of i into the string that you are sending to OpenFile(). You have to create a string with a textual representation of i embedded in it, which this code does not do.
Change
OpenFile("b%i.txt")
to
char filename[8];
sprintf(filename, "b%d.txt", i); // Create filename with embedded number
ifstream OpenFile(filename);
ifstream OpenFile("b%i.txt");
You're not writing in Batch! Here you can't put number variable using %name syntax.
If you're using C++11, I'd recommend to use std::to_string and type:
ifstream OpenFile("b"+std::to_string(i)+".txt");
for the code line where you are reading the characters in array
"OpenFile >> number [l];"
the compiler will through the error "segmentation fault" due to array out of bound.
so you have to add condition like this
if( l <= 14) // bcz you are starting with l=0
{
OpenFile >> number [l];
}
else
break;

Read from a file. C++

SO when my program starts, it attempts to read a list of products from a file. but if the file does not exist it displays an error and continue on. the problem im having is when it displays the error, it doesnt continue on to the do while loop
ifstream input;
input.open("data.txt");
if (input.fail())
{
cout << "\n Data file not found \n";
}
ListItemType data;
input >> data.productname;
while(( !input.eof()))
{
input >> data.category;
input >> data.productprice;
addproduct(head, data);
input >> data.productname;
}
input.close();
It's not identical functionality, but it's generally better to move towards something like:
if (std::ifstream input("data.txt"))
{
ListItemType data;
while (input >> data.productname >> data.category >> data.productprice >> data.productname)
addproduct(head, data);
if (!input.eof())
std::cerr << "Error parsing input file.\n";
}
else
cout << "\n Data file not found \n";
If you structure your if/else clauses as above, whatever happens it will continue to the following code as you'd like.
Note that the code above checks for a problem after each input operation. Your code tries to read data.productprice even if reading data.category failed. It's kind of weird you're reading productname twice, and I'm assuming you can call addproduct after the I/O - if not you'll need a while loop like:
while (input >> data.productname >> data.category >> data.productprice)
{
addproduct(head, data);
if (!(input >> data.productname))
break;
}

C++ file reading

I have a file that has a number in which is the number of names that follow. For example:
4
bob
jim
bar
ted
im trying to write a program to read these names.
void process_file(ifstream& in, ofstream& out)
{
string i,o;
int tmp1,sp;
char tmp2;
prompt_user(i,o);
in.open (i.c_str());
if (in.fail())
{
cout << "Error opening " << i << endl;
exit(1);
}
out.open(o.c_str());
in >> tmp1;
sp=tmp1;
do
{
in.get(tmp2);
} while (tmp2 != '\n');
in.close();
out.close();
cout<< sp;
}
So far I am able to read the first line and assign int to sp
I need sp to be a counter for how many names. How do I get this to read the names.
The only problem I have left is how to get the names while ignoring the first number.
Until then i cannot implement my loop.
while (in >> tmp1)
sp=tmp1;
This successfuly reads the first int from the and then tries to continue. Since the second line is not an int, extraction fails, so it stops looping. So far so good.
However, the stream is now in fail state, and all subsequent extractions will fail unless you clear the error flags.
Say in.clear() right after the first while loop.
I don't really see why you wrote a loop to extract a single integer, though. You could just write
if (!(in >> sp)) { /* error, no int */ }
To read the names, read in strings. A loop is fine this time:
std::vector<std::string> names;
std::string temp;
while (in >> temp) names.push_back(temp);
You'd might want to add a counter somewhere to make sure that the number of names matches the number you've read from the file.
int lines;
string line;
inputfile.open("names.txt");
lines << inputfile;
for(i=0; i< lines; ++i){
if (std::getline(inputfile, line) != 0){
cout << line << std::endl;
}
}
First of all, assuming that the first loop:
while (in >> tmp1)
sp=tmp1;
Is meant to read the number in the beginning, this code should do:
in >> tmp1;
According to manual operator>>:
The istream object (*this).
The extracted value or sequence is not returned, but directly stored
in the variable passed as argument.
So don't use it in condition, rather use:
in >> tmp1;
if( tmp1 < 1){
exit(5);
}
Second, NEVER rely on assumption that the file is correctly formatted:
do {
in.get(tmp2);
cout << tmp2 << endl;
} while ( (tmp2 != '\n') && !in.eof());
Although whole algorithm seems a bit clumsy to me, this should prevent infinite loop.
Here's a simple example of how to read a specified number of words from a text file in the way you want.
#include <string>
#include <iostream>
#include <fstream>
void process_file() {
// Get file name.
std::string fileName;
std::cin >> fileName;
// Open file for read access.
std::ifstream input(fileName);
// Check if file exists.
if (!input) {
return EXIT_FAILURE;
}
// Get number of names.
int count = 0;
input >> count;
// Get names and print to cout.
std::string token;
for (int i = 0; i < count; ++i) {
input >> token;
std::cout << token;
}
}

Problems reading first line from file after 'getline'

After inputting getline, the first row of numbers in my input file are ignored and the program starts at the second line until the end.
Here's the code:
while (!file.eof())
{
getline(file, lineBuffer);
if(lineBuffer.length()==0)
{
continue; //ignore empty lines
}
else
{
// file.open("test.txt");
while (file >> A >> B >> N)
{
for(int count=1; count<=N; count++)
{
if(count%A == 0 && count%B == 0) { cout << "FB "; }
else if(count%A == 0) { cout << "F "; }
else if(count%B == 0) { cout << "B "; }
else { cout << count << " "; }
}
cout << endl;
}
}
}
The input file contains 3 rows of integers and only the second line and third like are processed. I've looked this up extensively, but no one has mentioned a case similar to mine. Usually they deal with people trying to get user input after a getline function. Any ideas would be greatly appreciated.
getline(file, lineBuffer) reads a line from file and stores it in lineBuffer. If you want to use it, you should then process lineBuffer instead of reading from file (where the first line will already be skipped). To make it work, simply remove all code outside of the inner while. This will read from the file 3 numbers at a time, stopping when extracting one of them fails.
Is the above code real or is it an example?
If it is real, you don't need to skip th eempty lines. The operator>> will skip the whitespaces for you.
The following code without any lineskipping performs just the same reading of "A B N" lines:
// outer loop is not needed, either remove it, or include error checking
// while (!file.eof() && !file.fail())
{
while (file >> A >> B >> N)
{
for(int count=1; count<=N; count++)
{
if(count%A == 0 && count%B == 0) { cout << "FB "; }
....
If for some reason you cannot remove the getline at all, remember that getline reads the line ALWAYS (well, unless the streampointer reached its end). This means that your code skips the empty lines, then reads first nonempty line, notices that the line is not empty and breaks the loop. Note that it has read the line, hence, your first-line-of-data now is in the linebuffer, and not in the stream anymore. Now you have to either unget the whole line (if your stream supports it) or rewind the stream by the line's length (if your stream supports it) or - just read the data from the line - for example using istringstream class.
In general, mixing getline and operator>> is doable but tricky, (especially when you first read via >> and then want to skip a 1 or 2 lines), as they behave a little differently in terms of buffering and whitespace-handling. If you really want that, search for this subject - there are lots of examples how to sync them.