11
\$\begingroup\$

I have a text file that contains name, age, salary, hoursWorked, randomText and are filled with different delimiters.

Text file:

susan:25-2600,28[asd]
mary:21-2200,38[asd]
john:23-3400,46[asd]

Instead of breaking them into individual strings using the code shown below:

string name,age,salary,hoursWorked,randomText;
ifstream readFile("textfile.txt");

while(getline(readFile,line))   {
    stringstream iss(line);
    getline(iss, name, ':');
    getline(iss, age, '-');
    getline(iss, salary, ',');
    getline(iss, hoursWorked, '[');
    getline(iss, randomText, ']');
}
readFile.close();

What are some better strategies other than coding it this way?

Side note

I declared all the variables to strings because of the getline() method.

\$\endgroup\$
13
\$\begingroup\$

I would create a class and define an input operator:

struct Person
{
    std::string name;
    std::string age;
    std::string salary;
    std::string hoursWorked;
    std::string randomText;

    friend std::istream& operator>>(std::istream& str, Person& data)
    {
        std::string line;
        Person tmp;
        if (std::getline(str,line))
        {
            std::stringstream iss(line);
            if ( std::getline(iss, tmp.name, ':')        && 
                 std::getline(iss, tmp.age, '-')         &&
                 std::getline(iss, tmp.salary, ',')      &&
                 std::getline(iss, tmp.hoursWorked, '[') &&
                 std::getline(iss, tmp.randomText, ']'))
             {
                 /* OK: All read operations worked */
                 data.swap(tmp);  // C++03 as this answer was written a long time ago.
             }
             else
             {
                 // One operation failed.
                 // So set the state on the main stream
                 // to indicate failure.
                 str.setstate(std::ios::failbit);
             }
        }
        return str;
    }
    void swap(Person& other) throws() // C++03 as this answer was written a long time ago.
    {
        swap(name,        other.name);
        swap(age,         other.age);
        swap(salary,      other.salary);
        swap(hoursWorked, other.hoursWorked);
        swap(randomText,  other.randomText)
    }
};

Now your code looks like this:

Person   data;
while(readFile >> data)
{
    // Do Stuff
}

PS. I noticed you were using string and ifstream without the std::. This suggests you have using namespace std; in your code. Please don't do that. see Why is “using namespace std;” considered bad practice?

Don't explictly close() a file unless you are going to check that it worked (or are going the re-open). Prefer to let the destructor do the closing (that way it is exception safe). See: My C++ code involving an fstream failed review

\$\endgroup\$
  • \$\begingroup\$ In the operator>> definition I needed to add the class before the member variables (e.g. data.name, data.age) in the getline calls for this to compile. \$\endgroup\$ – Praxeolitic Mar 26 '14 at 12:37
  • \$\begingroup\$ @user39469: Sorry. Fixed. \$\endgroup\$ – Martin York Mar 26 '14 at 14:45
  • \$\begingroup\$ Nice. The only thing I would change: I would use a local Person tmp; instance, and in the if code block (the "OK: All read operations worked" part) I would write data = std::move(tmp); (or std::swap(data, tmp);).. This way, you avoid the case when the stream doesn't actually contain a valid person (in that case, if one of the getline calls fails, data will not be partially filled with garbage). \$\endgroup\$ – utnapistim Mar 26 '14 at 15:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.