How to group values from the same line into a pair? - c++

I am trying to write a program that reads a text file which has a data set in the form below and puts every 2 integers per line into a pair:
1 2
3 4
5 6
... and so on
Currently, this part of my main code reads the file line by line and converts the imported strings to integers one number at a time, but I am not sure how I would group 2 integers at a time in each line and put them in a pair. Ultimately, I want to add all the pairs to a single set.
while (getline(fs, line)) {
istringstream is(line);
while(is >> line) {
int j = atoi(line.c_str());
cout << "j = " << j << endl;}}

You could simply use std::pair, like this:
istringstream is(line);
pair<int, int> p;
is >> p.first;
is >> p.second;
cout << p.first << " " << p.second;
In next step you could use std::set<std::pair<int, int> > to acheive your goal of putting the pairs into single set.

Try this:
std::ifstream ifs{ "yourfile.txt" };
// read line by line
std::string line;
while(std::getline(ifs, line)) {
std::istringstream iss{ line }; // make a stream on the line
auto values = std::make_pair<int, int>(0, 0);
if(iss >> values.first >> values.second) { // check stream state
// after reading two ints
// (this skips spaces)
std::cout << "a=" << values.first << ", b=" << values.second << "\n";
} else {
throw std::runtime_error{ "bad data" };
}
// at this point, you have read a valid pair of ints into "values"
}
See the code comments for explanations.

Use pair available in 'utility' header, following is the sample code for the same.
while (getline(fs, line))
{
stringstream is;
is<<test;
int i, j;
is>>i;
is>>j;
pair<int, int> pr(i,j);
}

Following may help:
std::string line;
std::set<std::pair<int, int>> myset;
while (std::getline(fs, line)) {
std::istringstream is(line);
int a, b;
if (is >> a >> b) {
myset.insert({a, b});
} else {
throw std::runtime_error("invalid data");
}
}
Live example
To consider 1, 5 as identical as 5, 1, you may use:
struct unordered_pair_comp
{
bool operator () (const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const
{
return std::minmax(lhs.first, lhs.second) < std::minmax(rhs.first, rhs.second);
}
};
and so your myset becomes std::set<std::pair<int, int>, unordered_pair_comp> myset;
Live example

Something like this should work for your purposes.
while (getline(fs, line))
{
istringstream is(line);
int i, j;
is >> i >> j;
std::pair<int,int> my_pair = std::make_pair (i, j);
}

Dont make the imported strings into integers. Read the two strings from one line and concatenate them. Then make them integers. Like this:
#include <sstream>
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
int main()
{
string line;
ifstream fs("input.txt");
while (getline(fs, line))
{
istringstream is(line);
string num1, num2;
while (is >> num1 >> num2)
{
num1 += num2;
int j = stoi(num1);
cout << "j = " << j << endl;
}
cin.get();
}
}

Related

Problem reading file and storing it in unordered_map

I'm trying to read a file and store it's content into an unordered_map but I've got a little problem. This is my unordered_map:
std::unordered_map<std::string, std::vector<double>> _users;
And this is the content of the file that I'm trying to read:
Mike 4 NA 8 NA NA
Lena NA 8 4 NA 9
I want to store the content in _users in a way that the key is the name, and inside the vectors we have the numbers associated to the name. Moreover I want NA to be equal to 0.
So I managed to do this:
while ( std::getline(file, line))
{
std::istringstream iss(line);
std::string key;
double value;
iss >> key;
dict[key] = std::vector<double>();
while (iss >> value)
{
dict[key].push_back(value);
}
}
But since value is a double, when checking NA it just stops the while loop and I just get, for example with Mike: Mike 4. How can I do in order to get it to read NA and put it as 0 inside the vector ? Thank you for your help!
For your inner loop, you could do:
std::string stringval;
while (iss >> stringval)
{
double value;
try
{
value = std::stod (stringval);
}
catch (...)
{
value = 0.0;
}
dict[key].push_back(value);
}
I also tried something:
#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
int main(int argc, char*[])
{
//1. Read the file and load the map
std::unordered_map<std::string, std::vector<double>> users{};
std::ifstream file{"file.txt"};
std::string line{};
while(std::getline(file, line))
{
std::istringstream iss(line);
std::string key{};
iss >> key;
std::vector<double> values{};
while(iss)
{
double value{0};
if (iss.str() != "NA")
iss >> value;
values.push_back(value);
}
users.insert({key, values});
}
//2. Printing the map
for(auto const &[key, values]: users)
{
std::cout << "Key: " << key << std::endl;
for(auto const value: values)
{
std::cout << value << " ";
}
std::cout << std::endl;
}
return EXIT_SUCCESS;
}

C++, reading doubles from txt into vector, problems with reading the whole file

I have a text file with 2 columns of doubles with a tab in between, and try to read them into 2 vectors. My first problem is that it does not go through the whole file but starts in the last third. My second problem is that while it does push_back it converts the numbers in some other numbers.. I just can't get my head around it..
If I try to just put them all into one string vector it works without problems, but I need them as doubles or int for further processing
ifstream myfile("TextFile",ios::in);
if (!myfile)
{
cout << "Can't open" << endl;
system("pause");
return -1;
}
vector<long double> Datenx;
vector<long double> Dateny;
vector<string>lel;
string line;
while (getline(myfile, line)) {
// lel.push_back(line);
string numberx = line.substr(0, 12);
int pos = line.find(" ");
string numbery = line.substr(pos + 1, 12);
stringstream iss(numberx);
long double x = 0.0;
iss>> setprecision(10)>>fixed >>showpoint >> x;
//cout <<fixed<< numberx << endl;
//cout<<setprecision(10)<<fixed<< x << endl;
Datenx.push_back(x);
stringstream is(numbery);
long double y = 0.0;
is >> y;
Dateny.push_back(y);
}
for (int n = 0; n < 100; n++) {
cout << Datenx[n] << ' ' << endl;
}
// cout << fixed << Datenx[2] << ' ' << endl;
cin.get();
return 0;
Part of input file:
0.0000000000 0.0006536954
0.0000000100 0.0005515555
0.0000000200 0.0005004856
0.0000000300 0.0001327819
0.0000000400 0.0006945514
0.0000000500 0.0007864773
0.0000000600 0.0001327819
0.0000000700 0.0007354074
Output: Datenx vector:
0
1e-08
2e-08
3e-08
...
Output: Dateny vector:
0.000653695
0.000551555
0.000500486
0.000132782
so the Dateny is kinda right.. it cuts the last digit
and the Datenx vector is total wrong..
Try to keep it simple first. If it works, you can add functionality.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
int main()
{
std::vector<double> v1, v2;
std::string line;
std::ifstream myFile("input.txt");
while(getline(myFile, line))
{
std::istringstream lineStream(line);
double first, second;
lineStream >> first >> second;
v1.push_back(first);
v2.push_back(second);
}
}
I tried this, with the following "input.txt"
1.1 1.2
2.1 2.2
3.1 3.2
4.1 4.2
5.1 5.2

How to determine last string value in a text

I'm given a text in std::string that i want to analyze using stringstream.
The text is a line from a csv file in the following format:
SPIN;5;WIN;10;STOPPOSITIONS;27;1;14
I must create a key value pair (in a map) with the key being a string value from the line (ex: "SPIN") and the value a vector populated with the next integer value from the line (ex: 5). (KVP: {"SPIN", {5}}).
The problem is that I dont know how to determine the last string value of the line (in this example "STOPPOSITIONS").
When i get the word "STOPPOSITIONS" at the next iteration the variable word is changed to "1" which is wrong because i should create the following kvp (KVP: {"STOPPOSITIONS", {27,1,14}}).
What should i fix in order to find the last string value of a line?
Here is the code I'm using:
std::map<std::string, std::vector<uint64_t>> CsvReader::readAllKvp()
{
if (!_ifs->is_open())
{
_ifs->open(_fileName);
}
std::map<std::string, std::vector<uint64_t>> result;
std::string line;
std::string word;
uint64_t val;
while(getline(*_ifs,line,'\n') >> std::ws)
{
/* do stuff with word */
std::istringstream ss(line);
while(getline(ss, word, ';') >> std::ws)
{
//no more strings found
if(word == "")
{
//read all integers at the end of the line and put them
//in the map at the last key added (in our case: STOPPOSITIONS)
while(ss >> val)
{
result[result.rbegin()->first].push_back(val);
}
break;
}
if (result.find(word) == result.end()) //word not found in map
{
std::vector<uint64_t> newV;
result.insert(
std::pair<std::string, std::vector<uint64_t>>(word, newV));
}
ss >> val;
result[word].push_back(val);
ss.ignore(std::numeric_limits<std::streamsize>::max(),';');
}
}
_ifs->close();
return result;
}
I made an example of my suggested method. It only read one line, but adding another outer loop and processing all the lines of the file is a simple task.
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <vector>
using std::cout;
using std::endl;
std::map<std::string, std::vector<uint64_t>> readAllKvp()
{
std::string str = "SPIN;5;WIN;10;STOPPOSITIONS;27;1;14";
std::stringstream ss(str); // Emulating input from file
std::map<std::string, std::vector<uint64_t>> result;
std::string word;
std::string last_string;
uint64_t val;
while(getline(ss >> std::ws, word, ';') >> std::ws)
{
try {
val = std::stoi(word);
if(!last_string.empty())
result[last_string].push_back(val);
} catch (std::invalid_argument&) {
last_string = word;
}
}
return result;
}
int main() {
auto map = readAllKvp();
for (auto& m : map) {
cout << m.first << ": ";
for (auto v : m.second)
cout << v << ' ';
cout << endl;
}
}

Read matrix from text file using vectors in C++

we have a matrix in text file like this with commas between numbers, but there is no comma at the end of each line.
1,2,3,4
7,8,2,1
3,4,5,6
7,2,1,3
I was trying to do this with a 2D array like this but it wasn't really working out because also size of matrix is unknown.
string array[4][4];
int id;
for (int i = 0; i < 4; i++) { // go through each line
for (int j = 0; j < 4; j++) {
getline(filein, numbers, ',');
array[i][j] = numbers;
cout << array[i][j] << endl;
}
}
I want to do that with using 2D vectors but I have no idea how to do that. Like after creating a vector with
vector<vector<string>> matrix;
Should I create one more vector inside the loops?
Use vector of vectors. Here is the commentary for each line:
std::vector<std::vector<int>> v; // declare vector of vectors
std::ifstream ifs("myfile.txt"); // open the file
std::string tempstr; // declare a temporary string
int tempint; // declare a temporary integer
char delimiter; // declare a temporary delimiter
while (std::getline(ifs, tempstr)) { // read line by line from a file into a string
std::istringstream iss(tempstr); // initialize the stringstream with that string
std::vector<int> tempv; // declare a temporary vector for the row
while (iss >> tempint) { // extract the numbers from a stringstream
tempv.push_back(tempint); // push it onto our temporary vector
iss >> delimiter; // read the , delimiter
}
v.push_back(tempv); // push the vector onto vector of vectors
}
The full source code is:
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <string>
int main() {
std::vector<std::vector<int>> v;
std::ifstream ifs("myfile.txt");
std::string tempstr;
int tempint;
char delimiter;
while (std::getline(ifs, tempstr)) {
std::istringstream iss(tempstr);
std::vector<int> tempv;
while (iss >> tempint) {
tempv.push_back(tempint);
iss >> delimiter;
}
v.push_back(tempv);
}
for (auto row : v) {
for (auto el : row) {
std::cout << el << ' ';
}
std::cout << "\n";
}
}
Use a double std::vector, read the file line by line, and parse the commas as well, and you are done. Every time you read a line, increase the vector's size by 1. You can do that with std::vector::resize().
Example, using your file as "matrix.txt":
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <vector>
int main(void) {
std::vector<std::vector<int>> matrix;
std::ifstream infile("matrix.txt");
int a, b, c, d;
char comma;
while (infile >> a >> comma >> b >> comma >> c >> comma >> d)
{
//std::cout << a << " " << b << " " << c << " " << d << std::endl;
matrix.resize(matrix.size() + 1);
matrix[matrix.size() - 1].push_back(a);
matrix[matrix.size() - 1].push_back(b);
matrix[matrix.size() - 1].push_back(c);
matrix[matrix.size() - 1].push_back(d);
}
for(auto row: matrix) {
for(auto v: row) {
std::cout << v << " ";
}
std::cout << "\n";
}
return 0;
}
Output:
Georgioss-MacBook-Pro:~ gsamaras$ g++ -std=c++0x main.cpp
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out
1 2 3 4
7 8 2 1
3 4 5 6
7 2 1 3

Parsing through text file with C++

I am trying to figure out the best way to read in numbers from a text file and set these numbers to variables. I am having trouble because there will be multiple text files that will be testing my code and they are all of different lengths and sizes. A sample test one looks like this:
0 (1,3) (3,5)
1 (2,6)
2 (4,2)
3 (1,1) (2,4) (4,6)
4 (0,3) (2,7)
Where the first number represents a vertex on a graph, the first number in a coordinate is the vertex it is going towards in a directed graph, and the second number is the weight of the edge. I tried doing getline and putting it into arrays but in certain test cases there could be 100 coordinates and I am not sure how to specify array size. I am also having trouble parsing through the parenthesis and comma and am not sure how to initialize the variables with the correct number from the text file.
Parsing shouldn't be that difficult, espacially when you can use std::stringstream to separate all the elements from input. Indeed, you want to remove all the paranthesis first then emplace the elements into the container.
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
int main()
{
std::ifstream read("file.txt");
std::vector<std::vector<std::pair<int, int>>> graph;
// read until you reach the end of the file
for (std::string line; std::getline(read, line); ) {
// removing punctuation like paranthesis, commas, etc.
std::replace_if(std::begin(line), std::end(line), [] (char x) { return std::ispunct(x); }, ' ');
// inserting the line into a stream that helps us parse the content
std::stringstream ss(line);
// read the node number
int source, destination, weight;
ss >> source;
// create a new vector for the new node, so you can place all it's destinations / weights in it
graph.insert(std::next(std::begin(graph), source), {{}});
// read the dests / weights until you reach the end of the current line
while (ss >> destination >> weight)
graph[source].emplace_back(destination, weight);
}
read.close();
std::ofstream write("output.txt");
for (const auto node : graph) {
for (const auto [dest, weight] : node)
write << "(" << dest << ", " << weight << ") ";
write << '\n';
}
}
Note that you need C++17 to compile the code. You have to use basic loops instead of ranged-for loops and omit auto if you use an older C++ standard. Also I used a vector of pairs, but it's better if you use a struct / class for nodes, to make the code more maintanable.
This works too.
#include<iostream>
#include<sstream>
#include<string>
using namespace std;
int main() {
int vertices[1000][3], qv = 0; //use number more than 1000 if it is required
while (cin) {
int n;
char c;
string s;
getline(cin, s);
istringstream is(s);
is >> n;
is >> c;
while (c == '(') {
vertices[qv][0] = n;
is >> vertices[qv][1];
is >> c; //,
is >> vertices[qv++][2];
is >> c; //)
is >> c; //(
}
}
for (int i = 0; i < qv; i++) //unified view
cout << vertices[i][0] << ' ' << vertices[i][1] << ' ' << vertices[i][2] << endl;
for (int i = 0; i < qv; i++) { //initial view
cout << vertices[i][0];
cout << " (" << vertices[i][1] << "," << vertices[i][2] << ")";
while (i + 1 < qv && vertices[i][0] == vertices[i + 1][0]) {
i++;
cout << " (" << vertices[i][1] << "," << vertices[i][2] << ")";
}
cout << endl;
}
}
I would use something like this:
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#include <algorithm>
struct coordinate
{
int vertex;
int weight;
};
struct vertex_set
{
int vertex;
std::vector<coordinate> coordinates;
};
std::istream& operator>>(std::istream &in, coordinate &out)
{
char ch1, ch2, ch3;
if (in >> ch1 >> out.to_vertex >> ch2 >> out.weight >> ch3)
{
if ((ch1 != '(') || (ch2 != ',') || (ch3 != ')'))
in.setstate(std::ios_base::failbit);
}
return in;
}
std::istream& operator>>(std::istream &in, std::vector<coordinate> &out)
{
out.clear();
coordinate coord;
while (in >> coord)
out.push_back(coord);
return in;
}
std::istream& operator>>(std::istream &in, vertex_set &out)
{
return in >> out.vertex >> out.coordinates;
}
std::ifstream f("file.txt");
std::string line;
while (std::getline(f, line))
{
vertex_set vs;
if (std::istringstream(line) >> vs)
{
// use vs.vertex and vs.coordinates as needed...
}
}