How to save a specific column into an array in C++? - 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.
}
}

Related

Difference between CSV files which makes different outcome using getline()

I'm writing a function which reads a CSV file using getline() and converts data to the vector of vectors. To test it I've tried to read two files with the same delimiter: one imported from the internet and second exported from R datasets. The first few lines of each looks like:
File1.csv
User ID,Category 1,Category 2,Category 3,Category 4,Category 5,Category 6,Category 7,Category 8,Category 9,Category 10
User 1,0.93,1.8,2.29,0.62,0.8,2.42,3.19,2.79,1.82,2.42
User 2,1.02,2.2,2.66,0.64,1.42,3.18,3.21,2.63,1.86,2.32
User 3,1.22,0.8,0.54,0.53,0.24,1.54,3.18,2.8,1.31,2.5
User 4,0.45,1.8,0.29,0.57,0.46,1.52,3.18,2.96,1.57,2.86
File2.csv
"","Sepal.Length","Sepal.Width","Petal.Length","Petal.Width"
"1",5.1,3.5,1.4,0.2
"2",4.9,3,1.4,0.2
"3",4.7,3.2,1.3,0.2
"4",4.6,3.1,1.5,0.2
However getline() works only for the first one. In second case it simply returns white space. The function performs similar even if I copy single lines from one file to another (of course adding or removing additional colums) -- the rows from file1 will be always properly read while those from file2 never. I've even tried removing " chars, but without much improvement. However switching from comas to '\t' solves the problem.
I'm curious what's the difference between those two files that makes such different outcome?
The source code of my function:
vector<vector<string>> readData(string fileName,int firstLine,char delimeter){
//Open data file
fstream fin;
fin.open(fileName, ios::in);
//Data stored in 2d vector of strings
vector<vector<string>> data;
vector<string> row;
string line,word,temp;
//Read data
int i=0;
while(fin>>temp){
row.clear();
//Read line and store in 'line'
getline(fin,line);
//Don't read first n lines
if (i<firstLine){
i++;
continue;
}
cout<<line<<endl;
//Break words
stringstream s(line);
//Read every column and store in in 'word;
while(getline(s,word,delimeter)){
row.push_back(word);
}
//Append row to the data vector
data.push_back(row);
}
//Close file
fin.close();
return data;
}
The problem is here:
while(fin>>temp){
row.clear();
//Read line and store in 'line'
getline(fin,line);
fin >> temp reads everything till the first space or newline. It is not clear why you do that as only with getline(fin,line) you then try to read the full line and you are not using temp. In the first file fin>>temp consumes only "User", in the second file it consumes the full line, because there are no spaces.
If you look at the read data from the first file you will also notice that the first part of each line is missing.
Tip: Use more meaningful names for your variables. I didn't manage to fully understand your logic, because variables named s and the presence of row and line at the same time causes me headaces.

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++ - Opening text files sequentially

I have hundreds of .txt files ordered by number: 1.txt, 2.txt, 3.txt,...n.txt. In each file there are two columns with decimal numbers.
I wrote an algorithm that does some operations to one .txt file alone, and now I want to recursively do the same to all of them.
This helpful question gave me some idea of what I'm trying to do.
Now I'm trying to write an algorithm to read all of the files:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main ()
{
int i, n;
char filename[6];
double column1[100], column2[100];
for (n=1;n=200;n++)
{
sprintf(filename, "%d.txt", n);
ifstream datafile;
datafile.open(filename);
for (i=0;i<100;i++)
{
datafile >> column1[i] >> column2[i];
cout << column1[i] << column2[i];
}
datafile.close();
}
return 0;
}
What I think the code is doing: it is creating string names from 1.txt till 200.txt, then it opens files with these names. For each file, the first 100 columns will be associated to the arrays column1 and column2, then the values will be shown on the screen.
I don't get any error when compiling it, but when I run it the output is huge and simply won't stop. If i set the output to a .txt file it reaches easily some Gb!
I also tried decreasing the loop number and reduce the numbers of columns (to 3 or so), but I till get an infinite output. I would be glad if someone could point the mistakes I'm doing in the code...
I am using gcc 5.2.1 with Linux.
Thanks!
6-element array is too short to store "200.txt". It must be at least 8 elements.
The condition n=200 is wrong and is always true. It should be n<=200.
If all your files are in the same directory, you could also use boost::filesystem, e.g.:
auto path = "path/to/folder";
std::for_each(boost::filesystem::directory_iterator{path},
boost::filesystem::directory_iterator{},
[](boost::filesystem::directory_entry file){
// test if file is of the correct type
// do sth with file
});
I think this is a cleaner solution.

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

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.

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