Reputation: 21
I have the following text file.
text.txt
1, Harry Potter, 1998, UK/trains/wizards/
The user is asked to enter the name of a book, then the date of publication, and lists any keywords to be associated with the book. There is no limit of the number of keywords the user can enter. This data will be used in a class with private members so that it can changed or removed etc.
I want to know how I could read the textfile that would split each line between the 1 Harry Potter 1998 and each of the keywords like UK trains wizards.
The code below reads the file and splits it according to a set delimter. Is ther a way to ammend this to work with multiple delimters or is the easy solution to create one file for the first bits of data and another for the keyowrds?
std::ifstream file("test.txt");
std::string line;
if (file)
{
std::string token;
std::stringstream ss;
while (getline(file, line))
{
ss << line;
while (getline(ss, token, ','))
{
std::cout << token << std::endl;
}
ss.clear();
}
}
Upvotes: 2
Views: 198
Reputation: 21576
Assuming you know the exact format of your text file, Thomas Matthews solution is probably better, but for a more general case where you don't know... You can try this solution, it works well with any set of delimeters and it works just like like getline...
std::istream& getline2(std::istream& stream, std::string& s, const std::string& delimeters){
s.clear(); char c;
while(stream.read(&c, 1)){
if(delimeters.find(c) != std::string::npos)
break;
s += c;
}
return stream;
}
Example Usage:
while (getline2(ss, token, ",/\."))
{
std::cout << token << std::endl;
}
Full code for your use case here on Coliru.
Upvotes: 0
Reputation: 173
Use strtok in this case
`std::ifstream file("test.txt");
std::string line;
if (file)
{
std::string token;
std::stringstream ss;
while (getline(file, line))
{
char * pch;
pch = strtok (str.c_str()," ,.-");
while (pch != NULL)
{
std::cout<<pch<<std::endl;
pch = strtok (NULL, " ,.-");
}
ss.clear();
}
}`
Upvotes: 0
Reputation: 73627
You could just extend your while
loop that goes through any number of fields, and break each of these down further using the second delimiter in an inner loop:
while (getline(file, line)) {
ss << line;
while (getline(ss, token, ',')) {
std::stringstream ss2(token); // parse now the field that was read
while (getline(ss2, tok2, '/')) // and decompose it again
std::cout << tok2 << " + ";
std::cout << std::endl;
}
ss.clear();
}
You could store the multivalued fields using a vector instead of just a string.
Upvotes: 0
Reputation: 33982
Simple solution is to do exactly what you did to split on the comma on the keywords token:
std::vector parseKeywords(const std::string & keywords)
{
std::vector result;
std::stringstream keywordstrm(token);
std::string keyword;
while (getline(keywordstrm, keyword, '/'))
{
result.push_back(keyword);
}
return result;
}
Upvotes: 0
Reputation: 57749
Don't use a loop for the comma separated fields. Use a loop for the keywords.
std::string token1 = getline(ss, token1, ','); // 1
std::string token2 = getline(ss, token2, ','); // "Harry Potter"
std::string token3 = getline(ss, token3, ','); // 1998
std::vector<string> keywords;
std::string word;
while (getline(ss, word, '/'))
{
keywords.push_back(word);
}
You need to limit the number of extractions based on the comma delimiter. Since there are only 3 columns in your example, there really isn't a need for a loop.
Upvotes: 1