Input from text file, not displaying correctly, why? - c++

Here is some part of my main:
int main() {
Inventory Master;
bool flag;
Customer Bob("Bob", "CreditCard.txt");
Customer Chris("Chris", "CreditCard.txt" );
}
Here is my method:
Customer::Customer( string n, string fileName ) {
name = n;
ifstream Credit;
Credit.open(fileName.c_str(), ios::in);
while( Credit.good() && !Credit.eof() ) {
Credit >> card >> balance >> ws;
cout << card <<"\t" << balance << endl;
}
CreditCard _CC( int card, double balance);
}
Here is my "CreditCard.txt file:
12345 15.00
32564 20.00
The way I wanted the info to display is have line 1 "12345 15.00" assigned to Bob and line 2 assigned to Chris and do that so on and so forth if i make new instances or objects of a customer. However the way I currently implemented it is it keeps assigning "12345 15.00 and 32564 20.00" to both Bob and Chris. I could appreciate the help if someone could SHOW me how to somehow point to certain lines of the text file so Bob is assigned to line 1, Chris to line 2, and more customers to other lines when i add them in the text file.

Everything you're doing to Bob and Chris happens inside the constructor. So, as written, your code says: while the stream is in good condition and it's not the end of the file(key point), write to here.
Well, if you think about it, this will read until the end of the file is reached for each instance of Customer. That's not what you want. I might suggest adding the name as the first field in the data file for each record. You could then search the file for the correct record, assuming you ensure the names are all uniquely defined, then pull the data out string by string. That way it's not reading from the beginning to the end each time. I added "Bob" as the first field on line 1, and "Chris" to line 2 and made string name = "Chris";. So...
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
string tempStr;
string name = "Chris";
ifstream Credit;
Credit.open("Info.txt", ios::in);
while( Credit.good() && !Credit.eof() )
{
getline(Credit, tempStr, ' ');//Reads the first records name field
cout << tempStr << endl;
if(name.compare(tempStr) == 0)//Compares the "name" to the field.
{ //If true they are the same
//Proceed to do reading and assignments with additional getline statements
cout << "Chris was matched the second time around!";
Credit.setstate(ios::eofbit, true);//***Sets eof to true
}
else
{
Credit.ignore(50, '\n');
//That should put Credit in the proper position to read the next name
}
}
}
The way you're doing it will cause problems. The only way that it would work for sure is if you knew where the record was at in the file. What if you had five records? By the time you got to the third one you would have to ignore, or similar, all the fields prior to the one you're working on. Also, it could be handy for a human to read a print out of the data file. Another reason to provide a label(name) to each record. Also, you're apparently using namespace std;, so I did too, but it's frowned upon.

istream.getline() http://www.cplusplus.com/reference/iostream/istream/getline/ could be your answer. Just read one line at a time.
A little example here:
http://www.cplusplus.com/forum/beginner/27799/
Little Example from one of my old homerworks:
ifstream fin(fileName);
char buffer[256];
int count = 0;
if (fin.is_open())
{
while (!fin.eof())
{
fin.getline(buffer, 256);
}
}

Related

searching a name in the csv file on C++

I am a young programmer who is trying to learn c++. i have a working csv.file. but i want to search for a specific number assigned to the name and then displays the name of what i'm looking for. i have the file here:
1,Bulbasaur,grass
2,Ivysaur, grass
3,Venusaur, grass
4,Charmander, fire
5,Charmeleon, fire
6,Charizard, fire
7,Squirtle, water
8,Wartortle, water
9,Blastoise, water
Code
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream ip("pokedex.csv");
string pokedexnum[9];
string pokemonName[9];
string pokemonType[9];
cout<<"please enter a pokemon number:"<<" ";
cin>>pokemonType[0];
while (ip.good()){
getline( ip, pokedexnum[0]);
getline( ip, pokemonName[0]);
getline( ip, pokemonType[0]);
}
cout<<"the pokemon that is:"<< " "<<pokedexnum[0]<< "is the pokemon called:"<< pokemonName[0];
ifstream close("pokedex.csv");
return 0;
}
when it runs
please enter a pokemon number: 1
the pokemon that is: is the pokemon called:8,Wartortle, water
could you please point out what i am doing wrong?
Among the issues in this code:
You're not using std::getline correctly for comma-separated data. The result is each pass is consuming three lines from your input file; not three values from each line.
You're also not using ip.good() correctly as a while-condition.
You're retaining your test value in the array, which will be overwritten on the first iteration pass, so it is lost.
You're ignoring potential IO failures with each std::getline invoke.
You're overwriting slot-0 in your arrays with each loop iteration.
Minor, ifstream close("pokedex.csv"); clearly isn't doing what you think it is. That just creates another fstream object called close on the given file name.
The later may be intentional for now, but clearly broken in the near future.
In reality, you don't need arrays for any of this. All you're doing is reading lines, and seem to want to test the input number against that of the CSV data first column, reporting the line that you find, then ending this.
So do that:
Read the input value to search for.
Open the file for scanning.
Enumerate the file one line at a time.
For each line from (3), use a string stream to break the line into the comma separated values.
Test the id value against the input from (1). If the same, report the result and break the loop; you're done.
The result is something like this:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdlib>
int main()
{
std::cout<<"please enter a pokemon number: ";
long num;
if (std::cin >> num && num > 0)
{
std::ifstream ip("pokedex.csv");
std::string line;
while (std::getline(ip, line))
{
std::istringstream iss(line);
std::string id, name, skill;
if (std::getline(iss, id, ',') &&
std::getline(iss, name, ',') &&
std::getline(iss, skill))
{
char *endp = nullptr;
long n = std::strtol(id.c_str(), &endp, 10);
if (id.c_str() != endp && n == num)
{
std::cout << "The pokemon that is: " << num << " is called: " << name << '\n';
break;
}
}
}
}
}
Admittedly untested, but it should work.
Whether you want to store the items in arrays at this point is entirely up to you, but it isn't needed to solve the somewhat abstract problem you seem to be attempting, namely finding the matching line and reporting the name from said-same. If you still want to store them in arrays, I suggest you craft a structure to do so, something like:
struct Pokemon
{
int id;
std::string name;
std::string skill;
};
and have a single array of those, rather than three arbitrary arrays that must be kept in sync.
Four issues jump out at me:
You store the user's input into pokemonType, but then also use pokemonType for reading data from your CSV file. The file input is going to overwrite the user input.
Your file input loop always references index 0. All of the lines from your data file are going into element 0. That's the main reason that even if the user inputs 1, the output is from the last line of the data file.
Your file reading loop is structured like you want to put one part of each data line into a different array, but what you've written actually reads three lines on every iteration, storing those lines into the three different arrays.
This isn't affecting your output, but the code ifstream close("pokedex.csv"); is written like you want to close the file stream you opened, but I do believe what this line actually does is create a new ifstream called close, and opens pokedex.csv attached to it. In other words, it's just like your other line ifstream ip("pokedex.csv"); but with close as the variable name instead of ip.
You are going to want to look into something called "string tokenization". Start with some web searches, apply what you read about to your code, and of course if you hit another snag, post a new question here to Stack Overflow, showing (as you did here) what you tried and in what way it isn't working.
Elaborating on #3, here's what how your data file is being read:
at the end of the 1st iteration of the file-reading loop, ...
pokedexnum[0] is "1,Bulbasaur,grass"
pokemonName[0] is "2,Ivysaur, grass"
pokemonType[0] is "3,Venusaur, grass"
at the end of the 2nd iteration of the file-reading loop, ...
pokedexnum[0] is "4,Charmander, fire"
pokemonName[0] is "5,Charmeleon, fire"
pokemonType[0] is "6,Charizard, fire"
at the end of the 3rd iteration of the file-reading loop, ...
pokedexnum[0] is "7,Squirtle, water"
pokemonName[0] is "8,Wartortle, water"
pokemonType[0] is "9,Blastoise, water"
And that's why
<< "is the pokemon called:"<< pokemonName[0];
outputs
is the pokemon called:8,Wartortle, water

(C++) - Associating strings with values read in from a file

I am trying to get a value associate with a string inside a file called invoice1.txt
invoice1.txt
hammer#10.00
saw#20.00
So for example, when I lookup "hammer" I would like the expression to evaluate to 10.00.
My code so far
string search;
ifstream inFile;
string line;
double price;
inFile.open("invoice1.txt");
if(!inFile)
{
cout << "Unable to open file" << endl;
return 0;
}
else
{
int pos;
while(inFile.good())
{
getline(inFile,line);
pos=line.find(search);
if(pos!=string::npos)
{
cout<<"The item "<<search<<" costs: "// code to get the price
}
}
}
system("pause");
This is the output I am aiming for:
The item hammer costs: 10.00
The summerise, my question is:
How can I associate values with one another that are read in from a file, so I can get a price for an item without having to reparse the file and find it again?
This is what std::map is for.
What you want to do is break your problem down into multiple stages. Here is a simple set of steps that should help you (there are better ways, but I'm trying to keep things simple here).
I've added some lines to explain how to use std::map, in case you're not familiar.
Read the file line by line.
For each line that is read in, get the value after the '#' character.
Add the value to the map, using the string before '#' as the key...
priceMap[key] = price; // for example, this might evaluate to: myMap["hammer"] = 10.00
When you want to use the value, simple give the map you're key.
std::cout << priceMap["hammer"];
What do you search in line from file? You have to search for character # and split your string into two parts.
getline(inFile,line);
pos=line.find('#');
if(pos!=string::npos)
cout<<"The item "<<line.substr(0,pos)<<" costs: " << line.substr(pos+1,line.size()-1) << endl;// code to get the price
You can save item name and price in different variables if you want. If you want to do something more with a string, read this for further instructions.

reading file and split the line in c++

I have the following code that read input from txt file as follow
Paris,Juli,5,3,6
Paris,John,24,2
Canberra,John,4,3
London,Mary,29,4,1,2
my code is to load the data into map then I want to print the map content to make sure that it has been inserted correctly, I check the vaue of m as it is used during splitting the line. However, during the execution I get this as continues 0s which means it is never enter the while loop. I have used this part of code before and it works. I could not find where I've made the mistake.
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <vector>
#include<map>
using namespace std;
struct info {
string Name;
int places;// i will use the binary value to identfy the visited places example 29 is 100101
// this means he visited three places (London,LA,Rome)
vector<int> times; // will represent the visiting time,e.g. 1,2,5 means london 1 time, LA
// twice and Rome five times
};
map<string,vector<info> > log;
map<string,vector<info> >::iterator i;
fstream out;
int main() {
out.open("log.txt", std::ios::in | std::ios::out | std::ios::app);
string line;
char* pt;
string temp[19];
// for each line in the file
while (!out.eof())
{
getline(out,line);//read line from the file
pt=strtok(&line[0],"," );//split the line
int m=0;
while (pt != NULL)
{
temp[m++] = pt; // save the line info to the array
cout<<m<<" ";
pt = strtok (NULL, ",");
}
cout<<m<<" "; // during the execution I get this as continues 0s which means it is never enter the while loop
info tmp;
// read the other data
tmp.Name=temp[1];
tmp.places=atoi(temp[2].c_str());
for ( int i=3;i<=m;i++)
{
tmp.times.push_back(atoi(temp[i].c_str()));
}
// create a new object
log[temp[0]].push_back(tmp);
}
vector<int>::iterator j;
for(i=log.begin();i!=log.end();i++) {
cout<< "From "<< i->first<<" city people who travels: "<<endl;
for (size_t tt = 0; tt < (i->second).size(); tt++) {
cout<< (i->second[tt]).Name<< " went to distnations "<< (i->second)[tt].places<<" \nwith the folloing number of visiting time ";
for (j=((i->second[tt]).times).begin();j!= ((i->second[tt]).times).end();j++)
cout<<*j<<" ";
}
}
system("PAUSE");
return 0;
}
This is an error
// for each line in the file
while (!out.eof())
{
getline(out,line);//read line from the file
should be
// for each line in the file
while (getline(out,line))
{
I find it frankly incredible how often this error is repeated. eof does not do what you think it does. It tests if the last read failed because of end of file. You are using it to try and predict whether the next read will fail. It simply doesn't work like that.
This line is an error
pt=strtok(&line[0],"," );//split the line
strtok works on C strings, there's no guarantee it will work on std::string.
But neither of these are likely to be your real error. I would suggest opening the file with ios::in only. After all you only want to read from it.
Your fstream should not open in app mode. That will seek the file to the end of file. Delete std::ios::app from it.
You can't tokenize an std::string using strtok. Use getline instead:
std::string str("some,comma,separated,data");
std::string token;
while (getline(str, token, ',')) {
cout << "Token: " << token << end;
}
At each iteration, token contains the next parsed token from str.
This is wrong temp[m++] = pt; // save the line info to the array
Switch to something like this, instead of "temp"
std::vector<std::string> vTemp;
pt=strtok(&line[0],"," );//split the line
while (pt != NULL)
{
vTemp.push_back(pt); // save the line info to the array
pt = strtok (NULL, ",");
}
Also consider using something like this to do the split.
std::vector<std::string> SplitString(const std::string &strInput, char cDelimiter)
{
std::vector<std::string> vRetValue;
std::stringstream ss(strInput);
string strItem;
while(std::getline(ss, strItem, cDelimiter))
{
// Skip Empty
if(strItem.size()==0)
continue;
vRetValue.push_back(strItem);
}
return vRetValue;
}
#halfelf really great solution for my simple error, it works but the problem is now when I print the data I got this
From Paris city people who travels:
Juli went to distnations 5
with the folloing number of visiting time 3 6 0
John went to distnations 24
with the folloing number of visiting time 2 6
From Canberra city people who travels:
Johnwent to distnations 4
with the folloing number of visiting time 3 6
From London city people who travels:
Mary went to distnations 29
with the folloing number of visiting time 4 1 2 0
This is not correct as 6 is added to John from Canberra and Paris and 0 is added to Juli and Mary.
any idea of where I get it wrong ,, its about the times vector , its seems that I need to reset the value for each line or clear the content after the insertion. what about the extra 0?

For loops and inputing data?

trying to figure out how to make a little inventory program and I can't for the life figure out why it isn't working.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct record
{
int item_id;
string item_type;
int item_price;
int num_stock;
string item_title;
string item_author;
int year_published;
};
void read_all_records(record records[]);
const int max_array = 100;
int main()
{
record records[max_array];
read_all_records(records);
cout << records[2].item_author;
return 0;
}
void read_all_records(record records[])
{
ifstream invfile;
invfile.open("inventory.dat");
int slot = 0;
for (int count = 0; count<max_array; count++);
{
invfile >> records[slot].item_id >> records[slot].item_type >> records[slot].item_price >> records[slot].num_stock >> records[slot].item_title >> records[slot].item_author >> records[slot].year_published;
slot++;
}
invfile.close();
}
I'm testing it by having it print the second item from records author. When I run it, it doesn't show the authors name at all. The .dat file is located in just about every folder where the project is (I forgot which folder it needs to be in) so it's there.
The issue isn't that the file isn't working. It's the array not printing off anything.
my inv file is basically:
123456
book
69.99
16
title
etc
etc
and repeats for different books/cds etc all on one line, all without spaces. Should just next in.
You should check to see that the file is open.
invfile.open("inventory.dat");
if (!invfile.is_open())
throw std::runtime_error("couldn't open inventory file");
You should check to seen that your file reads are working and breaks when you hit the end of file.
invfile >> records[slot].item_id >> records[slot].item_type ...
if (invfile.bad())
throw std::runtime_error("file handling didn't work");
if (invfile.eof())
break;
You probably want to read each record at time, as it isn't clear from this code how the C++ streams are supposed to differentiate between each field.
Usually you'd expect to use std::getline, split the fields on however you delimit them, and then use something like boost::lexical_cast to do the type parsing.
If I were doing this, I think I'd structure it quite a bit differently.
First, I'd overload operator>> for a record:
std::istream &operator>>(std::istream &is, record &r) {
// code about like you had in `read_all_records` to read a single `record`
// but be sure to return the `stream` when you're done reading from it.
}
Then I'd use an std::vector<record> instead of an array -- it's much less prone to errors.
To read the data, I'd use std::istream_iterators, probably supplying them to the constructor for the vector<record>:
std::ifstream invfile("inventory.dat");
std::vector<record> records((std::istream_iterator<record>(invfile)),
std::istream_iterator<record>());
In between those (i.e., after creating the file, but before the vector) is where you'd insert your error handling, roughly on the order of what #Tom Kerr recommended -- checks for is_open(), bad(), eof(), etc., to figure out what (if anything) is going wrong in attempting to open the file.
Add a little check:
if (!invfile.is_open()) {
cout<<"file open failed";
exit(1);
}
So that way, you don't need to copy your input file everywhere like you do now ;-)
You are reading in a specific order, so your input file should have the same order and required number of inputs.
You are printing 3rd element of the struct records. So you should have at least 3 records. I don't see anything wrong with your code. It would a lot easier if you can post your sample input file.

What's the correct way to read a text file in C++?

I need to make a program in C++ that must read and write text files line by line with an specific format, but the problem is that in my PC I work in Windows, and in College they have Linux and I am having problems because of line endings are different in these OS.
I am new to C++ and don't know could I make my program able read the files no matter if they were written in Linux or Windows. Can anybody give me some hints? thanks!
The input is like this:
James White 34 45.5 10 black
Miguel Chavez 29 48.7 9 red
David McGuire 31 45.8 10 blue
Each line being a record of a struct of 6 variables.
Using the std::getline overload without the last (i.e. delimiter) parameter should take care of the end-of-line conversions automatically:
std::ifstream in("TheFile.txt");
std::string line;
while (std::getline(in, line)) {
// Do something with 'line'.
}
Here's a simple way to strip string of an extra "\r":
std::ifstream in("TheFile.txt");
std::string line;
std::getline(input, line));
if (line[line.size() - 1] == '\r')
line.resize(line.size() - 1);
If you can already read the files, just check for all of the newline characters like "\n" and "\r". I'm pretty sure that linux uses "\r\n" as the newline character.
You can read this page: http://en.wikipedia.org/wiki/Newline
and here is a list of all the ascii codes including the newline characters:
http://www.asciitable.com/
Edit: Linux uses "\n", Windows uses "\r\n", Mac uses "\r". Thanks to Seth Carnegie
Since the result will be CR LF, I would add something like the following to consume the extras if they exist. So once your have read you record call this before trying to read the next.
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
If you know the number of values you are going to read for each record you could simply use the ">>" method. For example:
fstream f("input.txt" std::ios::in);
string tempStr;
double tempVal;
for (number of records) {
// read the first name
f >> tempStr;
// read the last name
f >> tempStr;
// read the number
f >> tempVal;
// and so on.
}
Shouldn't that suffice ?
Hi I will give you the answer in stages. Please go trough in order to understand the code.
Stage 1: Design our program:
Our program based on the requirements should...:
...include a definition of a data type that would hold the data. i.e. our
structure of 6 variables.
...provide user interaction i.e. the user should be able to
provide the program, the file name and its location.
...be able to
open the chosen file.
...be able to read the file data and
write/save them into our structure.
...be able to close the file
after the data is read.
...be able to print out of the saved data.
Usually you should split your code into functions representing the above.
Stage 2: Create an array of the chosen structure to hold the data
...
#define MAX 10
...
strPersonData sTextData[MAX];
...
Stage 3: Enable user to give in both the file location and its name:
.......
string sFileName;
cout << "Enter a file name: ";
getline(cin,sFileName);
ifstream inFile(sFileName.c_str(),ios::in);
.....
->Note 1 for stage 3. The accepted format provided then by the user should be:
c:\\SomeFolder\\someTextFile.txt
We use two \ backslashes instead of one \, because we wish it to be treated as literal backslash.
->Note 2 for stage 3. We use ifstream i.e. input file stream because we want to read data from file. This
is expecting the file name as c-type string instead of a c++ string. For this reason we use:
..sFileName.c_str()..
Stage 4: Read all data of the chosen file:
...
while (!inFile.eof()) { //we loop while there is still data in the file to read
...
}
...
So finally the code is as follows:
#include <iostream>
#include <fstream>
#include <cstring>
#define MAX 10
using namespace std;
int main()
{
string sFileName;
struct strPersonData {
char c1stName[25];
char c2ndName[30];
int iAge;
double dSomeData1; //i had no idea what the next 2 numbers represent in your code :D
int iSomeDate2;
char cColor[20]; //i dont remember the lenghts of the different colors.. :D
};
strPersonData sTextData[MAX];
cout << "Enter a file name: ";
getline(cin,sFileName);
ifstream inFile(sFileName.c_str(),ios::in);
int i=0;
while (!inFile.eof()) { //loop while there is still data in the file
inFile >>sTextData[i].c1stName>>sTextData[i].c2ndName>>sTextData[i].iAge
>>sTextData[i].dSomeData1>>sTextData[i].iSomeDate2>>sTextData[i].cColor;
++i;
}
inFile.close();
cout << "Reading the file finished. See it yourself: \n"<< endl;
for (int j=0;j<i;j++) {
cout<<sTextData[j].c1stName<<"\t"<<sTextData[j].c2ndName
<<"\t"<<sTextData[j].iAge<<"\t"<<sTextData[j].dSomeData1
<<"\t"<<sTextData[j].iSomeDate2<<"\t"<<sTextData[j].cColor<<endl;
}
return 0;
}
I am going to give you some exercises now :D :D
1) In the last loop:
for (int j=0;j<i;j++) {
cout<<sTextData[j].c1stName<<"\t"<<sTextData[j].c2ndName
<<"\t"<<sTextData[j].iAge<<"\t"<<sTextData[j].dSomeData1
<<"\t"<<sTextData[j].iSomeDate2<<"\t"<<sTextData[j].cColor<<endl;}
Why do I use variable i instead of lets say MAX???
2) Could u change the program based on stage 1 on sth like:
int main(){
function1()
function2()
...
functionX()
...return 0;
}
I hope i helped...