AppleSh
AppleSh

Reputation: 21

C++ read file with multiple delimters

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

Answers (5)

WhiZTiM
WhiZTiM

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

Lucky Sharma
Lucky Sharma

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

Christophe
Christophe

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

Online demo

You could store the multivalued fields using a vector instead of just a string.

Upvotes: 0

user4581301
user4581301

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

Thomas Matthews
Thomas Matthews

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

Related Questions