Problem reading file and storing it in unordered_map - c++

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;
}

Related

Reading and storing every second line

I am creating a small search engine to find values in files and store them. I have a txt file with the data:
link1
cat, dog, bird
link2
car, wheel, chair
There is a code to read and store, but the index map is empty.
int function(string filename, map<string, set<string>>& index) {
int counter = 0;
set <string> tokens;
ifstream inStream;
inStream.open(filename);
if (inStream.fail()){
counter = 0;
}
string http, definition;
while (getline(inStream, http) && getline(inStream, definition)){
for (auto v : tokens){
index[v].insert(http);
counter++
}
}
return counter;
}
You forgot to split the items in each second line.
This is basically very simple.
You can use the well known std::getline with delimiter concept.
The corrected version of your code will be:
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <set>
std::istringstream fileSimulationStream{R"(link1
cat, dog, bird
link2
car, wheel, chair)"};
int main() {
// Resulting data
std::map<std::string, std::set<std::string>> data;
// Read all lines from file. always 2 lines in one shot
for (std::string link{}, item{}; std::getline(fileSimulationStream, link) and std::getline(fileSimulationStream, item); ) {
// Put line in an istringstream for further extraction
std::istringstream iss{item};
// Read the items
std::set<std::string> tempSet{}; std::string tempString{};
while (std::getline(iss, tempString, ',')) tempSet.insert(tempString);
// Now add everything to the map
data[link] = std::move(tempSet);
}
// Create some debug output
for (const auto&[key, set] : data) {
std::cout << key << "\t--> ";
for (const std::string s : set) std::cout << s << ' ';
std::cout << '\n';
}
}

Reading Triangle Data Mesh With Object Files

I need to load in the vertices of a .obj file in c++. I just copied over the verts from the object I downloaded so I didn't have to worry about texture mapping or anything. I was able to separate it into a single line and get rid of the v but am not able to isolate the x,y,z coordinates so I can assign it to three separate variables.
main.cpp
// ConsoleApplication3.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <memory>
#include <fstream>
#include <string>
using namespace std;
string pos1, pos2, pos3;
int main()
{
string line;
ifstream myfile("fox.txt");
if (myfile.is_open())
{
while (getline(myfile, line))
{
line.erase(std::remove(line.begin(), line.end(), 'v'), line.end());
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
fox.txt
v 10.693913 60.403057 33.765018
v -7.016389 46.160694 36.028797
v 9.998714 51.307644 35.496368
v -8.642366 49.095310 35.725204
A simple way to read in the line
v 10.693913 60.403057 33.765018
and separate it into 3 different variables is to first read in a char, then read in three doubles:
ifstream fin("fox.txt");
vector <vector<double>> data; // holds sets of coordinates
double a, b, c;
char v;
while(fin >> v >> a >> b >> c){
data.push_back({a, b, c});
}
If you wanted, you could also use std::stringstream to parse the input into doubles.
An easy way is to simply use std::stringstream and treat it like you would any other stream.
#include <sstream>
...
std::string pos1, pos2, pos3;
std::stringstream lineStream;
...
while (getline(myfile, line))
{
/* Make a string stream out of the line we read */
lineStream.str(line);
char skip; // Temp var to skip the first 'v'
lineStream >> skip >> pos1 >> pos2 >> pos3;
/* Reset error state flags for next iteration */
lineStream.clear();
}
Or you could avoid all that by using the >> operator on myfile directly.
std::string temp, pos1, pos2, pos3;
while (myfile >> temp >> pos1 >> pos2 >> pos3)
{
...
}
I'm going to figure you want to store this data in the likes of a std::vector. This is one way of doing it.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
const char* test_str = R"(
v 10.693913 60.403057 33.765018
v -7.016389 46.160694 36.028797
v 9.998714 51.307644 35.496368
v -8.642366 49.095310 35.725204
)";
struct data_item {
double x;
double y;
double z;
};
using data_set = std::vector<data_item>;
int main()
{
//std::ifstream myfile("fox.txt");
//if (!myfile.is_open()) {
// std::cout << "Unable to open file\n";
// return -1;
//}
std::stringstream as_file;
as_file << test_str;
data_set set;
for (; ;) {
std::string dummy;
data_item item;
as_file >> dummy >> item.x >> item.y >> item.z;
if (!dummy.size())
break;
set.push_back(item);
}
for (auto& item : set)
std::cout << item.x << " " << item.y << " " << item.z << std::endl;
return 0;
}
Don't do: using namespace std; It will save you a lot of headaches down the road. It also makes your code more readable to know stuff is out of the standard library.
When testing, it is sometimes more simple to use local data as I have with test_str. As pointed out in the comments, you could just let the stream do the conversion from text to doubles.
Note I've taken care of a failed file error in one place, the commented file stuff. Putting an else way down from the failure is not so clear and creates a large unneeded scope.

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

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

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();
}
}