I'm beginner in C++. Learning mostly from YouTube courses. I was doing the task when I came across the problem which stops me from finishing. I would like to ask for help with the part of code.
#include <iostream>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>
#include <ostream>
using namespace std;
int main()
{
srand(time(0));
ofstream file;
cout<<"TASK"<<"\n";
file.open("TASK.txt", fstream::in | fstream::out | fstream::trunc );
if(file.good() == true)
{
cout<<"TRUE"<<"\n";
char ch[] = {'#','#','*'}; // Array of three chars.
for(int i=0; i<10; i++)
{
for(int j=0; j<10; j++)
{
file<<ch[rand()%3]; //Random character
}
file<<endl;
}
file.seekp(rand()%120);
file.put('P');
file.close();
}
else cout<<"FALSE"<<"\n";
return 0;
}
What I've done:
Opened a file and created array for 3 characters from task description.
Created 2 for loops which write and format the file data.
Found out by using file.seekp(0,file.end) that the amount of characters in file equals 120.
Used knowledge of the amount of characters to file.seekp(rand()%120) which sets position in output sequence to random place.
Used file.put('P') to place character in that place.
Closed file.
What I don't know:
How to get rid of (or do something else to) these 20+ characters to change only these: '#', '#', '*'.
To visualise the problem:
OUTPUT: when it works as intended.
##*#*###*#
##*##*###*
##**##*###
###*###P**
****####*#
####*#####
**######*#
#*#*####*#
*####*##*#
##*#######
When it doesn't work as intended:
#######*#*
##*##*###*
######*#*#P
*###**###*
##*#*#*#**
**####*#**
#####*#***
###**####*
##*######*
####*#*###
If I wasn't clear enough I can try to explain more.
Solution (thanks to rustyx)
int r_number = 0;
for(int i=0; i<=120; i++)
{
int r_number2 = rand()%120;
if((r_number2%12)>=10)
{
r_number2 = rand()%120;
}
else r_number=r_number2;
}
Thank you.
The problem is that there are CR, LF characters (\r\n) at the end of each line, which shouldn't be touched. But you generate a random number between 0 and 119, which can hit those and damage the file.
I can think of 2 possible solutions:
loop and get a new random number if the remainder of dividing by 12 is >= 10 (you see why?)
get a random number between 0 and 99 and pad it for CR, LF: x += (x/10)*2
I don't know how VC++ handles file position in text mode, it might treat CR, LF as a single position. In that case adjust the padding logic from 12 to 11.
C++14
Generally, the staff in university has recommended us to use Boost to parse the file, but I've installed it and not succeeded to implement anything with it.
So I have to parse a CSV file line-by-line, where each line is of 2 columns, separated of course by a comma. Each of these two columns is a digit. I have to take the integral value of these two digits and use them to construct my Fractal objects at the end.
The first problem is: The file can look like for example so:
1,1
<HERE WE HAVE A NEWLINE>
<HERE WE HAVE A NEWLINE>
This format of file is okay. But my solution outputs "Invalid input" for that one, where the correct solution is supposed to print only once the respective fractal - 1,1.
The second problem is: The file can look like:
1,1
<HERE WE HAVE A NEWLINE>
1,1
This is supposed to be an invalid input but my solution treats it like a correct one - and just skips over the middle NEWLINE.
Maybe you can guide me how to fix these issues, it would really help me as I'm struggling with this exercise for 3 days from morning to evening.
This is my current parser:
#include <iostream>
#include "Fractal.h"
#include <fstream>
#include <stack>
#include <sstream>
const char *usgErr = "Usage: FractalDrawer <file path>\n";
const char *invalidErr = "Invalid input\n";
const char *VALIDEXT = "csv";
const char EXTDOT = '.';
const char COMMA = ',';
const char MINTYPE = 1;
const char MAXTYPE = 3;
const int MINDIM = 1;
const int MAXDIM = 6;
const int NUBEROFARGS = 2;
int main(int argc, char *argv[])
{
if (argc != NUBEROFARGS)
{
std::cerr << usgErr;
std::exit(EXIT_FAILURE);
}
std::stack<Fractal *> resToPrint;
std::string filepath = argv[1]; // Can be a relative/absolute path
if (filepath.substr(filepath.find_last_of(EXTDOT) + 1) != VALIDEXT)
{
std::cerr << invalidErr;
exit(EXIT_FAILURE);
}
std::stringstream ss; // Treat it as a buffer to parse each line
std::string s; // Use it with 'ss' to convert char digit to int
std::ifstream myFile; // Declare on a pointer to file
myFile.open(filepath); // Open CSV file
if (!myFile) // If failed to open the file
{
std::cerr << invalidErr;
exit(EXIT_FAILURE);
}
int type = 0;
int dim = 0;
while (myFile.peek() != EOF)
{
getline(myFile, s, COMMA); // Read to comma - the kind of fractal, store it in s
ss << s << WHITESPACE; // Save the number in ss delimited by ' ' to be able to perform the double assignment
s.clear(); // We don't want to save this number in s anymore as we won't it to be assigned somewhere else
getline(myFile, s, NEWLINE); // Read to NEWLINE - the dim of the fractal
ss << s;
ss >> type >> dim; // Double assignment
s.clear(); // We don't want to save this number in s anymore as we won't it to be assigned somewhere else
if (ss.peek() != EOF || type < MINTYPE || type > MAXTYPE || dim < MINDIM || dim > MAXDIM)
{
std::cerr << invalidErr;
std::exit(EXIT_FAILURE);
}
resToPrint.push(FractalFactory::factoryMethod(type, dim));
ss.clear(); // Clear the buffer to update new values of the next line at the next iteration
}
while (!resToPrint.empty())
{
std::cout << *(resToPrint.top()) << std::endl;
resToPrint.pop();
}
myFile.close();
return 0;
}
You do not need anything special to parse .csv files, the STL containers from C++11 on provide all the tools necessary to parse virtually any .csv file. You do not need to know the number of values per-row you are parsing before hand, though you will need to know the type of value you are reading from the .csv in order to apply the proper conversion of values. You do not need any third-party library like Boost either.
There are many ways to store the values parsed from a .csv file. The basic "handle any type" approach is to store the values in a std::vector<std::vector<type>> (which essentially provides a vector of vectors holding the values parsed from each line). You can specialize the storage as needed depending on the type you are reading and how you need to convert and store the values. Your base storage can be struct/class, std::pair, std::set, or just a basic type like int. Whatever fits your data.
In your case you have basic int values in your file. The only caveat to a basic .csv parse is the fact you may have blank lines in between the lines of values. That's easily handled by any number of tests. For instance you can check if the .length() of the line read is zero, or for a bit more flexibility (in handling lines with containing multiple whitespace or other non-value characters), you can use .find_first_of() to find the first wanted value in the line to determine if it is a line to parse.
For example, in your case, your read loop for your lines of value can simply read each line and check whether the line contains a digit. It can be as simple as:
...
std::string line; /* string to hold each line read from file */
std::vector<std::vector<int>> values {}; /* vector vector of int */
std::ifstream f (argv[1]); /* file stream to read */
while (getline (f, line)) { /* read each line into line */
/* if no digits in line - get next */
if (line.find_first_of("0123456789") == std::string::npos)
continue;
...
}
Above, each line is read into line and then line is checked on whether or not it contains digits. If so, parse it. If not, go get the next line and try again.
If it is a line containing values, then you can create a std::stringstream from the line and read integer values from the stringstream into a temporary int value and add the value to a temporary vector of int, consume the comma with getline and the delimiter ',', and when you run out of values to read from the line, add the temporary vector of int to your final storage. (Repeat until all lines are read).
Your complete read loop could be:
while (getline (f, line)) { /* read each line into line */
/* if no digits in line - get next */
if (line.find_first_of("0123456789") == std::string::npos)
continue;
int itmp; /* temporary int */
std::vector<int> tmp; /* temporary vector<int> */
std::stringstream ss (line); /* stringstream from line */
while (ss >> itmp) { /* read int from stringstream */
std::string tmpstr; /* temporary string to ',' */
tmp.push_back(itmp); /* add int to tmp */
if (!getline (ss, tmpstr, ',')) /* read to ',' w/tmpstr */
break; /* done if no more ',' */
}
values.push_back (tmp); /* add tmp vector to values */
}
There is no limit on the number of values read per-line, or the number of lines of values read per-file (up to the limits of your virtual memory for storage)
Putting the above together in a short example, you could do something similar to the following which just reads your input file and then outputs the collected integers when done:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
int main (int argc, char **argv) {
if (argc < 2) { /* validate at least 1 argument given for filename */
std::cerr << "error: insufficient input.\nusage: ./prog <filename>\n";
return 1;
}
std::string line; /* string to hold each line read from file */
std::vector<std::vector<int>> values {}; /* vector vector of int */
std::ifstream f (argv[1]); /* file stream to read */
while (getline (f, line)) { /* read each line into line */
/* if no digits in line - get next */
if (line.find_first_of("0123456789") == std::string::npos)
continue;
int itmp; /* temporary int */
std::vector<int> tmp; /* temporary vector<int> */
std::stringstream ss (line); /* stringstream from line */
while (ss >> itmp) { /* read int from stringstream */
std::string tmpstr; /* temporary string to ',' */
tmp.push_back(itmp); /* add int to tmp */
if (!getline (ss, tmpstr, ',')) /* read to ',' w/tmpstr */
break; /* done if no more ',' */
}
values.push_back (tmp); /* add tmp vector to values */
}
for (auto row : values) { /* output collected values */
for (auto col : row)
std::cout << " " << col;
std::cout << '\n';
}
}
Example Input File
Using an input file with miscellaneous blank lines and two-integers per-line on the lines containing values as you describe in your question:
$ cat dat/csvspaces.csv
1,1
2,2
3,3
4,4
5,5
6,6
7,7
8,8
9,9
Example Use/Output
The resulting parse:
$ ./bin/parsecsv dat/csvspaces.csv
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
Example Input Unknown/Uneven No. of Columns
You don't need to know the number of values per-line in the .csv or the number of lines of values in the file. The STL containers handle the memory allocation needs automatically allowing you to parse whatever you need. Now you may want to enforce some fixed number of values per-row, or rows per-file, but that is simply up to you to add simple counters and checks to your read/parse routine to limit the values stored as needed.
Without any changes to the code above, it will handle any number of comma-separated-values per-line. For example, changing your data file to:
$ cat dat/csvspaces2.csv
1
2,2
3,3,3
4,4,4,4
5,5,5,5,5
6,6,6,6,6,6
7,7,7,7,7,7,7
8,8,8,8,8,8,8,8
9,9,9,9,9,9,9,9,9
Example Use/Output
Results in the expected parse of each value from each line, e.g.:
$ ./bin/parsecsv dat/csvspaces2.csv
1
2 2
3 3 3
4 4 4 4
5 5 5 5 5
6 6 6 6 6 6
7 7 7 7 7 7 7
8 8 8 8 8 8 8 8
9 9 9 9 9 9 9 9 9
Let me know if you have questions that I didn't cover or if you have additional questions about something I did and I'm happy to help further.
I will not update your code. I look at your title Parsing a CSV file - C++ and would like to show you, how to read csv files in a more modern way. Unfortunately you are still on C++14. With C++20 or the ranges library it would be ultra simple using getlines and split.
And in C++17 we could use CTAD and if with initializer and so on.
But what we do not need is boost. C++`s standard lib is sufficient. And we do never use scanf and old stuff like that.
And in my very humble opinion the link to the 10 years old question How can I read and parse CSV files in C++? should not be given any longer. It is the year 2020 now. And more modern and now available language elements should be used. But as said. Everybody is free to do what he wants.
In C++ we can use the std::sregex_token_iterator. and its usage is ultra simple. It will also not slow down your program dramatically. A double std::getline would also be ok. Although it is not that flexible. The number of columns must be known for that. The std::sregex_token_iterator does not care about the number of columns.
Please see the following example code. In that, we create a tine proxy class and overwrite its extractor operator. Then we us the std::istream_iterator and read and parse the whole csv-file in a small one-liner.
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <regex>
#include <string>
#include <vector>
// Define Alias for easier Reading
// using Columns = std::vector<std::string>;
using Columns = std::vector<int>;
// The delimiter
const std::regex re(",");
// Proxy for the input Iterator
struct ColumnProxy {
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, ColumnProxy& cp) {
// Read a line
std::string line;
cp.columns.clear();
if(std::getline(is, line) && !line.empty()) {
// Split values and copy into resulting vector
std::transform(
std::sregex_token_iterator(line.begin(), line.end(), re, -1), {},
std::back_inserter(cp.columns),
[](const std::string& s) { return std::stoi(s); });
}
return is;
}
// Type cast operator overload. Cast the type 'Columns' to
// std::vector<std::string>
operator Columns() const { return columns; }
protected:
// Temporary to hold the read vector
Columns columns{};
};
int main() {
std::ifstream myFile("r:\\log.txt");
if(myFile) {
// Read the complete file and parse verything and store result into vector
std::vector<Columns> values(std::istream_iterator<ColumnProxy>(myFile), {});
// Show complete csv data
std::for_each(values.begin(), values.end(), [](const Columns& c) {
std::copy(c.begin(), c.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
});
}
return 0;
}
Please note: There are tons of other possible solutions. Please feel free to use whatever you want.
EDIT
Because I see a lot of complicated code here, I would like to show a 2nd example of how to
Parsing a CSV file - C++
Basically, you do not need more than 2 statements in the code. You first define a regex for digits. And then you use a C++ language element that has been exactly designed for the purpose of tokenizing strings into substrings. The std::sregex_token_iterator. And because such a most-fitting language element is available in C++ since years, it would may be worth a consideration to use it. And maybe you could do basically the task in 2 lines, instead of 10 or more lines. And it is easy to understand.
But of course, there are thousands of possible solutions and some like to continue in C-Style and others like more moderen C++ features. That's up to everybodies personal decision.
The below code reads the csv file as specified, regardless of how many rows(lines) it contains and how many columns are there for each row. Even foreing characters can be in it. An empty row will be an empty entry in the csv vector. This can also be easly prevented, with an "if !empty" before the emplace back.
But some like so and the other like so. Whatever people want.
Please see a general example:
#include <algorithm>
#include <iterator>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
// Test data. Can of course also be taken from a file stream.
std::stringstream testFile{ R"(1,2
3, a, 4
5 , 6 b , 7
abc def
8 , 9
11 12 13 14 15 16 17)" };
std::regex digits{R"((\d+))"};
using Row = std::vector<std::string>;
int main() {
// Here we will store all the data from the CSV as std::vector<std::vector<std::string>>
std::vector<Row> csv{};
// This extremely simple 2 lines will read the complete CSV and parse the data
for (std::string line{}; std::getline(testFile, line); )
csv.emplace_back(Row(std::sregex_token_iterator(line.begin(), line.end(), digits, 1), {}));
// Now, you can do with the data, whatever you want. For example: Print double the value
std::for_each(csv.begin(), csv.end(), [](const Row& r) {
if (!r.empty()) {
std::transform(r.begin(), r.end(), std::ostream_iterator<int>(std::cout, " "), [](const std::string& s) {
return std::stoi(s) * 2; }
); std::cout << "\n";}});
return 0;
}
So, now, you may get the idea, you may like it, or you do not like it. Whatever. Feel free to do whatever you want.
For my master Thesis I need to write a program in C++ but I am not at all a programmer. It is also my first program in C++, I used to program for school in Java.
My problem is the following.
I have these textfiles containing some data, looking like this:
In case you can't see the picture:
index date month WaveState WindState
0 2015-01-01 00:00:00 1 9.0 8.0
1 2015-01-01 04:00:00 1 9.0 7.0
2 2015-01-01 08:00:00 1 9.0 8.0
3 2015-01-01 12:00:00 1 9.0 9.0
4 2015-01-01 16:00:00 1 9.0 8.0
5 2015-01-01 20:00:00 1 9.0 7.0
6 2015-01-02 00:00:00 1 9.0 4.0
7 2015-01-02 04:00:00 1 9.0 2.0
8 2015-01-02 08:00:00 1 9.0 1.0
9 2015-01-02 12:00:00 1 9.0 3.0
10 2015-01-02 16:00:00 1 9.0 4.0
and so on.
Now i need to extract from these textfiles only the numbers considering 'windstate' and 'wavestate'. I would like to write these to a vector so I can easily use them further in my program.
This is the code I wrote so far:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
vector <string> dataVector;
int main()
{
string line;
ifstream myfile("wm994.txt");
if(myfile.is_open())
{
while (getline(myfile,line))
{
dataVector.push_back(line);
}
myfile.close();
}
else cout << "Woops, couldn't open file!" << endl;
for (unsigned i = 0; i<dataVector.size(); i++)
{
cout << dataVector.at(i) << endl;
}
return 0;
}
But of course my result looks like this. In case you can't see the picture I will describe it for you. I think that in every location of the vector an entire row of the textfile is saved as a String. But how can I access the 2 separate parts 'winddata' and 'wavedata' then? I hope to write something that puts every seperate part of the textfile on a seperate location in the vector so i know what locations to access then to get my winddata or wavedata numbers. But I really don't know how to do this.. I tried something like this:
while (myfile)
{
// read stuff from the file into a string and print it
myfile>> dataVector;
}
But this of course isn't working. Can I do something like this? That only the white space between my seperate pieces of text is skipped and these pieces are located in a new location in the vector?
I really hope someone can help me with this problem. I feel completely lost. Thank you in advance.
You should split each line you get from the file and access the 3rd (WaveState) and 4th (WindState) element of the vector that you would get back. Here is an example on how to split a string.
String Split
If you compile support C++11 offer opportunities to use regex library:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <regex>
int main()
{
std::vector <std::pair<double,double>> dataVector;
std::string line;
std::ifstream myfile("1.txt");
std::smatch m;
std::regex e (".*(\\w+\\.\\w+) (\\w+\\.\\w+)$");
if(myfile.is_open())
{
while (getline(myfile,line))
{
std::regex_search (line,m,e);
dataVector.push_back(std::pair<double,double>(std::stod(m[1].str()),std::stod(m[2].str())));
}
myfile.close();
}
else
{
std::cout << "Woops, couldn't open file!" << std::endl;
return -1;
}
for (auto i:dataVector)
std::cout<<i.first<<" "<<i.second<<std::endl;
return 0;
}
I'm wondering if anybody can help me solving the following problem in C++:
I have a file where some time there is missing data i.e there are two consecutive TABs, then I need to transform the second TAB into "-999999" or "0" for example.
here is how the file looks like
i_1 i_2 i_3 i_4 i_5
j_1 12 14 16
j_2 11 17 25
j_3 44 51 65
I want to compute the mean of the elements on the first row i.e( 12,14 and 16) as:
sum+=tab[i][j];
mean = sum/5; (considering empty spaces =0)
thank you
#include <boost/algorithm/string/split.hpp>
#include <iostream>
#include <fstream>
#include <vector>
#include <list>
bool const compress_tokens = false;
bool const table_width = ...;
std::ifstream inp("filename");
// parsed grid
std::list<std::vector<std::string> > table;
std::string strbuf;
std::vector<std::string> vecbuf;
while(inp.getline(strbuf))
{
vecbuf.clear();
boost::split(vecbuf, strbuf, boost::is_any_of("\t"), compress_tokens);
assert(vecbuf.size() == table_width);
table.push_back(vecbuf);
}
Example of Textfile:
5 <- I need to edit this number.
0
1
0
6
(Sample Code Not Whole Program)
#include <fstream>
#include <iostream>
using namespace std;
int main() {
int i;
cin>>i;
std::fstream file("example.txt", std::ios::in | std::ios::out | std::ios::app);
file.seekp(0);
file << i;
return 0;
}
With this code the number is added here:
(example.txt)
5
0
1
0
67 <<
Please note that from the bottom the numbers will keep increasing so it has to be always the first line not that specific 5.
Please Help
Thanks
You have opened the file in a mode that requests that all new data is appended to the end of the file (std::ios::app). Don't specify that flag if you don't want to always append.
Note that you will encounter problems if the new line you're writing is not exactly the same length as the existing line. In the case where it's a different length, you will have to copy and rewrite the entire remainder of the file.