I'm loading a txt file into a vector using the code I've written below. It works great except for one BIG issue i'm having. First I'll give a very small example of a text file being converted.
12,26,1,3-4-71,13-19
21,56,8,12-9-35-7,16
There are more lines and more numbers per line, but kept example small.
Now, the BIG issue I don't know how to solve is: I get negative values in the vector, due to the "-" being present in the text file. I would like to keep the code as simple and fast as possible. I could eventually be using a txt file with 100,000+ lines. I want the " - " to be treated as a space. In the first line not -4 just 4, not a -19 just 19. In the vector they are showing up as negative numbers. Hope thats clear enough example.
std::ifstream infile;
std::string wpfile;
std::cout << "Enter the name of your Properties File : ";
std::cin >> wpfile;
infile.open(wpfile);
if (!infile) {
std::cerr << "Problem opening file" << std::endl;
}
int numberoflines {0};
while (std::getline(infile, line))
++numberoflines;
std::cout << "Your Properties File has " << numberoflines << " lines" << std::endl;
infile.clear();
infile.seekg(0);
std::cout << "\n Loading Your File.....\n";
std::vector<std::vector<int>> wp (numberoflines, std::vector<int>(65));
// Somewhere below here is where I think the change can be made
while (!infile.eof()) {
for (int i {0}; i < numberoflines ; ++i) {
for (int j {0}; j < 65; ++j) {
infile >> wp[i][j];
}
}
}
infile.close();
* If you read each char sequence between the commas into strings and turn them into std::istringstream you could use std::getline(stream, numberStr, '-') to extract each number (as a string) and then you could use std::istringstream again or std::stoi to convert numberStr into an integer.
* Another alternative, if commas and minus signs should be treated the same, is to replace the commas and/or minus signs in the line string using std::replace so that they become the same character. Then you could turn it into a stream and easily extract each number using getline (if you turn them into spaces you could use >> directly).
#include <iostream>
#include <regex>
#include <string>
#include <fstream>
int main()
{
const std::regex rgx("\\d+"); // <-- matches any sequence of "digit" characters
const std::sregex_token_iterator end;
std::ifstream infile;
infile.open("C:\\Temp\\test.txt");
if (!infile) {
std::cerr << "Problem opening file!" << std::endl;
}
std::vector<std::vector<int>> values;
std::string line;
while (std::getline(infile, line)) {
std::cout << "line str: \"" << line << '"' << std::endl;
if (values.empty() || (!values.back().empty())) {
values.emplace_back();
}
for (std::sregex_token_iterator iter(line.begin(), line.end(), rgx); iter != end; ++iter) {
if (iter->length() > 0) {
std::cout << "line tok: \"" << *iter << '"' << std::endl;
values.back().push_back(std::stoi(*iter));
}
}
}
}
Output:
line str: "12,26,1,3-4-71,13-19"
line tok: "12"
line tok: "26"
line tok: "1"
line tok: "3"
line tok: "4"
line tok: "71"
line tok: "13"
line tok: "19"
line str: "21,56,8,12-9-35-7,16"
line tok: "21"
line tok: "56"
line tok: "8"
line tok: "12"
line tok: "9"
line tok: "35"
line tok: "7"
line tok: "16"
line str: "12 91;87#21"
line tok: "12"
line tok: "91"
line tok: "87"
line tok: "21"
____
Also, why do you read (parse) the entire file twice? There is no need to read every line just to determine the total number of lines! Apparently you do this to allocate the std::vector and then you re-start from the beginning in order to fill that vector. Instead, you can simply append your values to the std::vector as they come in; the vector grows as needed! This way, the file can be parsed using a single pass.
Thanks to everyone who gave replies to my question. Guess I'm way over my head with this one. I'm very new to the learning in c++. I'm just kind of a hobbyist, learning my way online through udemy. Don't really grasp any of the answers that were given. I mean no disrespect to anyone, just clueless myself.
The output that seeplus got was exactly what I was hoping to get. That looks exactly what i want loaded into the vector. My apologies, I just don't understand the code. Maybe i should just play around with a single program to replace the commas and - with a "space" from a txt file then use that text file. Sorry, just sounds easier to do.
#include <string>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <sstream>
#include <iostream>
int main()
{
// 1. make a stream which gets its data from the file
std::ifstream input_file_stream{ "test_data.txt" };
// 2. read the entire file into the string file_contents
std::string file_contents(
std::istreambuf_iterator<char>{input_file_stream}, {});
// 3. replace commas with spaces in the file_contents:
std::replace(std::begin(file_contents), std::end(file_contents), ',', ' ');
// 4. replace dashes with spaces in the file_contents
std::replace(std::begin(file_contents), std::end(file_contents), '-', ' ');
// At this point, file_contents contains a copy of the text in the file,
// without any spaces or dashes within it.
// 5. make a stream which gets its data from the file_contents string:
std::istringstream file_contents_stream {file_contents};
// 6. Treat file_contents_stream exactly like you would input_file_stream.
std::vector<std::vector<int>> numbers_in_file;
for (std::string line; std::getline(file_contents_stream, line); )
{
std::istringstream line_stream{line};
std::vector<int> numbers_in_line;
for (int n; line_stream >> n; )
numbers_in_line.push_back(n);
numbers_in_file.push_back(numbers_in_line);
// If you're feeling fancy, use this as the loop body:
// std::istringstream line_stream{line};
// numbers_in_file.emplace_back(
// std::istream_iterator<int>{line_stream},
// std::istream_iterator<int>{});
}
std::cout << "your file contains " << numbers_in_file.size() << " lines\n";
std::cout << "the numbers are as follows:\n";
for (std::size_t line = 0; line < numbers_in_file.size(); ++line)
{
for (std::size_t i = 0; i < numbers_in_file[line].size(); ++i)
std::cout << numbers_in_file[line][i] << ' ';
std::cout << '\n';
}
}
Another approach could be, read all the numbers as in the original post, but make another pass through the vector to multiply all the negative numbers by -1.
My code from above can be simplified as strtol() terminates on a non-digit char and can return a pointer to this position. Consider where any non-digit can be used as a separator:
#include <fstream>
#include <iostream>
#include <vector>
int main()
{
std::ifstream infile;
std::string wpfile;
std::string line;
std::cout << "Enter the name of your Properties File : ";
std::cin >> wpfile;
infile.open(wpfile);
if (!infile) {
std::cerr << "Problem opening file" << std::endl;
}
int numberoflines {0};
while (std::getline(infile, line))
++numberoflines;
std::cout << "Your Properties File has " << numberoflines << " lines" << std::endl;
infile.clear();
infile.seekg(0);
std::cout << "\n Loading Your File.....\n";
std::vector<std::vector<int>> wp (numberoflines, std::vector<int>(65));
// Somewhere below here is where I think the change can be made
while (infile) { // check for end of file or error
for (int i {0}; i < numberoflines ; ++i) {
for (int j {0}; j < 65; ++j) {
// skip commas, whitespace and minus sign
while (infile >> std::ws && (infile.peek() == '-' || infile.peek() == ',')) {
infile.get();
}
// read the next number
infile >> wp[i][j];
}
}
}
infile.close();
// Print the results
for (auto &lineVec : wp) {
for (auto & num : lineVec) {
std::cout << num << ' ';
}
std::cout << '\n';
}
}
I was able to spend some time this evening to try the suggestions out.
Since I'm really green, I was able to follow/understand dhaydens code a little easier than others. NO DISRESPECT to anyone who replied. I'm just very new at this. Anyway, it worked as I needed it to. I did printout the other suggestions for further reference and understanding of the code. Probably when I get further along in the course on udemy, I will look back at those code printouts and say "UGH! Now I get what that code means."
THANKS to everyone!!
This board has always helped in the past. You guys are great!