reading from a file and storing it in a structure(not properly reading the file) - c++

The problem with my code is that in the while loop, the program is not properly reading the data in the file. If I were to output each individual member of the structure out of the while loop it would output stuff like zero, blank and even random numbers to different members of the structure. This also means that nothing is getting added to the vector because the vector size is zero.
Note 1-I'm using codeblocks as my IDE.
Note 2-The file that I'm using is an excel file. This means that I'm assuming that you've worked with excel files before and that you know that they're lined up in columns and rows. Also, I only want a certain amount of data going to each member in the structure.
Here's a very small sample from my input file.
EVENT_ID CZ_NAME_STR BEGIN_DATE BEGIN_TIME
9991511 MIAMI-DADE CO. 10/18/1955 800
9991516 MIAMI-DADE CO. 4/10/1956 1730
9991517 MIAMI-DADE CO. 4/10/1956 1730
Here's my code
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <vector>
#include <string>
using namespace std;
// Structure.
struct Weather_Event
{
int eventID; // is the unique int id associated with the weather event.
string CZ_NAME; // is the location associated with the weather event.
char beginDate[11]; // is the date the event started.
char beginTime[5]; // is the time the event started.
string eventType; // is the type of weather event.
int deaths; // are the number of people killed by the weather.
int injuries; // are the number of people injured by the event.
int propertyDamage; /* is the $ worth of property damage caused
by the event. */
float beginLatitude; // is the starting latitude for the event.
float beginLongitude; // is the starting longitude for the event.
};
int main()
{
// Create an empty vector that will contain the structure.
vector<Weather_Event>weatherInformation(0);
Weather_Event data; /* Create an object to access each member in
the structure. */
// Declare an object and open the file.
ifstream weather("weatherdata.csv");
if(!weather) // Check to see if the file opens.
{
cerr << "Error, opening file. ";
exit(EXIT_FAILURE); // terminate the program early.
}
/* While you're not at the end of the file, keep reading each new
piece of data. */
while(weather >> data.eventID >> data.CZ_NAME >> data.beginDate
>> data.beginTime >> data.eventType >> data.deaths
>> data.injuries >> data.propertyDamage >> data.beginLatitude
>> data.beginLongitude)
{
/* Add all the data that was added to each member of the
structure, into the vector. */
weatherInformation.push_back(data);
}
weather.close();
// Next display the result
for(size_t i=0; i<weatherInformation.size(); i++)
{
cout << "EventID: " << weatherInformation[i].eventID << endl;
}
return 0;
}

For starters, the format of the input doesn't seem to contain all the fields being read. That is, the input contains columns for event ID, name, begin data, and begin time but the code also reads event type, deaths, injuries, property damage, latitude, and longitude. Once this problem is address, you'll run into different other problems, though:
The first line doesn't match the format you are trying to read at all. You'll need to ignore it, e.g., using weather.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
The formatted input operator for string reads a single word. However, your name consists of multiple words, i.e., only the first of these words is read. I don't know if your file contains a suitable separators (e.g. tab characters '\t') you could use to delimit the name or if the name as a fixed length or something. You'll need something to tell the stream where to stop reading the name.
When reading into a char array using the formatted input operators, you should set up a limit for the characters to be read to avoid an overflow: if you don't set up a limit, as many characters as matching the format will be read! That is, you want to read the beginDate and the beginTime using something like this:
weather >> std::setw(sizeof(data.beginDate)) >> data.beginDate
>> std::setw(sizeof(data.beginTime)) >> data.beginTime;
Setting the width limits the amount of data being read. If there are more characters the input will fail rather than overflowing the buffer (if I recall correctly).
These are the problems I spotted based on the code and data you posted.

Related

How to save a specific column into an array in C++?

I have a set of data in a .txt file that has an arbitrary number of columns, specified by the user in the input. I want to read that file, pick one of the columns and save it in an array. What is the best way to do this?
I have read this, this and this, but they all establish in the code the specific number of columns. I want it to be some input, so that the code is "general" and saves in that array the specific column from the input. Thank you!
EDIT: This is an example of how the input looks like - the total number of Columns (particles) is specified by the user. The output will be some other .txt of data coming from this one.
TIME PART1 PART2 PART3 PART4
0 0.0147496 934.902 0.0949583 -1192.37 0.0141576 950.604 0.0905118 -1074.44
1.66667e-005 0.0147497 2804.7 0.0949583 -3577.12 0.0141576 2851.81 0.0905117 -3223.33
3.33333e-005 0.0147497 4674.5 0.0949582 -5961.86 0.0141577 4753.02 0.0905116 -5372.21
5e-005 0.0147498 6544.3 0.094958 -8346.6 0.0141578 6654.22 0.0905115 -7521.09
6.66667e-005 0.01475 8414.09 0.0949578 -10731.3 0.0141579 8555.41 0.0905114 -9669.96
I assume the user enters the coumn number over the console. So you can use the built-in cin function to read the input. You can use for loop and string streams to get the values. Code below; although you may have tweek it a little bit as per your needs
Edit: The code below has been edited a little bit. Now it should answer your question.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
int n;
cin >>n; //user needs to input the column number
fstream newfile;
//newfile.open("file.txt",ios::out); // open the file to perform write operation using file object; Activate this function if you want to overwrite
newfile.open("file.txt",ios::in); //open the file to perform read operation using file object
if (newfile.is_open())
{
string line;
getline(newfile, line); //skipping the first line
while(getline(newfile, line))//loop throuhg rest of the lines
{
int temp=n;
while(temp != 0)//loop until you get to the requied column
{
getline(newfile, line, '\t'); //get the vaue separted by tab='\t'. Be sure that the last column also ends in '\t'
temp--;
}
cout<<line<<endl; //now line holds the element on the loop-row of the selected column
}
newfile.close(); //close the file object.
}
}

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

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...

read in values and store in list in c++

i have a text file with data like the following:
name
weight
groupcode
name
weight
groupcode
name
weight
groupcode
now i want write the data of all persons into a output file till the maximum weight of 10000 kg is reached.
currently i have this:
void loadData(){
ifstream readFile( "inFile.txt" );
if( !readFile.is_open() )
{
cout << "Cannot open file" << endl;
}
else
{
cout << "Open file" << endl;
}
char row[30]; // max length of a value
while(readFile.getline (row, 50))
{
cout << row << endl;
// how can i store the data into a list and also calculating the total weight?
}
readFile.close();
}
i work with visual studio 2010 professional!
because i am a c++ beginner there could be is a better way! i am open for any idea's and suggestions
thanks in advance!
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <limits>
struct entry
{
entry()
: weight()
{ }
std::string name;
int weight; // kg
std::string group_code;
};
// content of data.txt
// (without leading space)
//
// John
// 80
// Wrestler
//
// Joe
// 75
// Cowboy
int main()
{
std::ifstream stream("data.txt");
if (stream)
{
std::vector<entry> entries;
const int limit_total_weight = 10000; // kg
int total_weight = 0; // kg
entry current;
while (std::getline(stream, current.name) &&
stream >> current.weight &&
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n') && // skip the rest of the line containing the weight
std::getline(stream, current.group_code))
{
entries.push_back(current);
total_weight += current.weight;
if (total_weight > limit_total_weight)
{
break;
}
// ignore empty line
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
else
{
std::cerr << "could not open the file" << std::endl;
}
}
Edit: Since you wannt to write the entries to a file, just stream out the entries instead of storing them in the vector. And of course you could overload the operator >> and operator << for the entry type.
Well here's a clue. Do you see the mismatch between your code and your problem description? In your problem description you have the data in groups of four lines, name, weight, groupcode, and a blank line. But in your code you only read one line each time round your loop, you should read four lines each time round your loop. So something like this
char name[30];
char weight[30];
char groupcode[30];
char blank[30];
while (readFile.getline (name, 30) &&
readFile.getline (weight, 30) &&
readFile.getline (groupcode, 30) &&
readFile.getline (blank, 30))
{
// now do something with name, weight and groupcode
}
Not perfect by a long way, but hopefully will get you started on the right track. Remember the structure of your code should match the structure of your problem description.
Have two file pointers, try reading input file and keep writing to o/p file. Meanwhile have a counter and keep incrementing with weight. When weight >= 10k, break the loop. By then you will have required data in o/p file.
Use this link for list of I/O APIs:
http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx
If you want to struggle through things to build a working program on your own, read this. If you'd rather learn by example and study a strong example of C++ input/output, I'd definitely suggest poring over Simon's code.
First things first: You created a row buffer with 30 characters when you wrote, "char row[30];"
In the next line, you should change the readFile.getline(row, 50) call to readFile.getline(row, 30). Otherwise, it will try to read in 50 characters, and if someone has a name longer than 30, the memory past the buffer will become corrupted. So, that's a no-no. ;)
If you want to learn C++, I would strongly suggest that you use the standard library for I/O rather than the Microsoft-specific libraries that rplusg suggested. You're on the right track with ifstream and getline. If you want to learn pure C++, Simon has the right idea in his comment about switching out the character array for an std::string.
Anyway, john gave good advice about structuring your program around the problem description. As he said, you will want to read four lines with every iteration of the loop. When you read the weight line, you will want to find a way to get numerical output from it (if you're sticking with the character array, try http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/, or try http://www.cplusplus.com/reference/clibrary/cstdlib/atof/ for non-whole numbers). Then you can add that to a running weight total. Each iteration, output data to a file as required, and once your weight total >= 10000, that's when you know to break out of the loop.
However, you might not want to use getline inside of your while condition at all: Since you have to use getline four times each loop iteration, you would either have to use something similar to Simon's code or store your results in four separate buffers if you did it that way (otherwise, you won't have time to read the weight and print out the line before the next line is read in!).
Instead, you can also structure the loop to be while(total <= 10000) or something similar. In that case, you can use four sets of if(readFile.getline(row, 30)) inside of the loop, and you'll be able to read in the weight and print things out in between each set. The loop will end automatically after the iteration that pushes the total weight over 10000...but you should also break out of it if you reach the end of the file, or you'll be stuck in a loop for all eternity. :p
Good luck!