I have a file where each row is separated by a '|' delimiter e.g:
1|23|1234|car|10
2|12|345|taxi|11
where each first value is unique. I want to read it into a map so that first value is the key and the following ones are a list of values for that key.
I have the following implementation, but I'm not sure if my way of locating the delimiter is correct or how to assign the needed value as key.
std::map<int, list<>> sample_map;
std::ifstream in("file.txt");
unsigned sum = 0;
string line;
while (getline(in, line)) {
const char *last = nullptr;
unsigned col = 0;
for (char &c : line)
if (c == '|') {
col;
if (col == 1) {
unsigned v;
from_chars(last, &c, v);
// assign v to key and rest of the data
// as values (excluding delimiter)
break;
}
}
}
CodePudding user response:
I highly recommend using a struct
or class
to model the input row (record):
struct Record
{
int id;
int column2;
int column3;
int name;
int last_column;
// Overload operator >> for this record.
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
char separator;
input >> r.id;
input >> separator;
input >> r.column2; input >> separator;
input >> r.column3; input >> separator;
std::getline(input, r.name, '|');
input >> r.last_column;
input.ignore(100000, '\n'); // Reset to next line.
return input;
}
Your input code could look like this:
std::map<int, Record> database;
Record r;
int id;
while (in >> r)
{
id = r.id;
database[id] = r;
}
There is a duplication of the ID in this implementation, but it uses the ID field as a key and you have your ID field in the record if you need it.
CodePudding user response:
I suggest adding a struct
to keep the data and to overload operator>>
to help with the input. I've also added a mini-struct to eat up the delimiters |
and \n
- which could be done purely with std::getline
but you'd then need to convert the integers so I decided to use the existing operator>>
to read integers from the stream.
Example:
#include <iostream>
#include <map>
#include <string>
//---------------------------------- a class to eat a delimiter
struct eat { char ch; };
// eat the expected character or set the stream in a failed state:
std::istream& operator>>(std::istream& is, const eat& e) {
if(is.peek() == e.ch) is.ignore();
else is.setstate(std::ios::failbit);
return is;
}
//----------------------------------
struct data { // the data the key in your map will map to
unsigned a;
unsigned b;
std::string c;
unsigned d;
};
// read one `data` from the stream:
std::istream& operator>>(std::istream& is, data& d) {
eat e{'|'}; // used to eat up `|`
return std::getline(is >> d.a >> e >> d.b >> e, d.c, '|') >> d.d >> eat{'\n'};
}
// print one `data`
std::ostream& operator<<(std::ostream& os, const data& d) {
return os << d.a << '|' << d.b << '|' << d.c << '|' << d.d << '\n';
}
Reading the map from the file and printing it:
#include <fstream>
int main() {
if(std::ifstream in("file.txt"); in) {
std::map<int, data> m;
int key;
data d;
// read one key and one `data` until that fails:
while(in >> key >> eat{'|'} >> d) {
m[key] = std::move(d); // put it in the map
}
// print the result:
for(auto&[k, v] : m) {
std::cout << k << '|' << v;
}
}
}