1

I am trying to read data from a comma delimited file into strings. Furthermore, I wish to remove extra spaces from string.

I have managed to implement a working solution, but I am interested if this can be done more efficiently. My main goal is to remove the temporary string initialization with default string size ( std::string lName( 100, 0 ); ) since data in the file is of variable length.

Also, if you have some constructive advice I would appreciate it.

I am using MS Visual Studio 2008.

Here is the SSCCE example :

#include <iostream>
#include <algorithm>
#include <string>
#include <fstream>

// helper function for removing extra spaces
void removeSpace( std::string &str )
{
    str.erase( std::remove( str.begin(), str.end(), ' ' ), str.end() );
}

int main()
{
    //========== let us construct a test file =====================//
    //===== format is Last Name, First Name, Gender, Color, Birth Date =======//
    std::ofstream os;
    os.open( "test.txt" );

    // first row
    os << " Smith  ," << " John  ," << "   Male , " 
        << " Green  , " << " 6  / 7 / 1960  \n";

    // second row
    os << " Mortensen ," << " Mike  ," << " Male  , " 
        << " Red  , " << "5/5/  1975 \n";

    // third row
    os << " Johnson ," << " Ann  ," << " Female , " 
        << " Blue , " << " 4/ 4 /1985 \n";

    os.close();

    // now let us read data from it
    std::ifstream g;
    g.open( "test.txt" );

    if( g.is_open() )
    {
        while( !g.eof() )
        {
            // temporary strings
            std::string lName( 100, 0 );
            std::string fName( 100, 0 );
            std::string gen( 100, 0 );
            std::string clr( 100, 0 );
            std::string date( 100, 0 );

            // get data from file
            g.getline( &lName[0], 100, ',' );
            g.getline( &fName[0], 100, ',' );
            g.getline( &gen[0], 100, ',' );
            g.getline( &clr[0], 100, ',' );
            g.getline( &date[0], 100 );

            // remove extra spaces from strings
            removeSpace( lName );
            removeSpace( fName );
            removeSpace( gen );
            removeSpace( clr );
            removeSpace( date );

            // display the result
            std::cout << lName.c_str() 
                << ' ' << fName.c_str() 
                << ' ' << gen.c_str()
                << ' ' << clr.c_str()
                << ' ' << date.c_str()
                << std::endl;

            //cleanup
            lName.clear();
            fName.clear();
            gen.clear();
            clr.clear();
            date.clear();
        }
        g.close();
    }

    // since our SSCCE example is done, let us delete the test file
    if( 0 != std::remove( "test.txt" ) )
        std::cout << "Couldn't delete test file!\n\n";
    else
        std::cout << "Successfully deleted test file!\n\n";

    return 0;
}

EDIT:

Following advice from member WhozCraig, I was able to produce an improvement. For brevity, I will only post the while loop:

while( !g.eof() )
{
    // temporary strings
    std::string line;

    if( ! std::getline( g, line ) )
        break;

    std::istringstream iss(line);

    while( iss )
    {
        std::string str;

        if ( ! std::getline( iss, str, ',' ) ) 
            break;

        // remove excess spaces
        removeSpace( str );
        // output the result
        std:: cout << str.c_str() << ' ';
    }

    std::cout << std::endl;
}
  • The writing of the textfile is not needed for the SSCCE, you can simply list the textfile, no? – flup May 24 '14 at 10:28
  • @flup: Those solutions use mainly Boost library, yet I can not use it. –  May 24 '14 at 10:34
  • 1
    The optional delimiter for [`std::getline`](http://en.cppreference.com/w/cpp/string/basic_string/getline) would be useful for this, especially via a string stream populated with the a full line read from the same function. But lose the `.eof()` check in your loop [for these reasons](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong). – WhozCraig May 24 '14 at 10:50
  • @WhozCraig: English is not my native tongue, can you please elaborate your comment? I haven't had the chance to work with stringstreams so far, that also adds to my inability to understand the comment. –  May 24 '14 at 10:52

1 Answers1

0

For trivial comma separation (as opposed to true-life CSV format which is well beyond the scope of your original question), judicious use of std::getline in conjunction with a std::istringstream can probably bail you out, especially when it comes to line-by-line qualification. I took liberty to fix the while-conditon as well,

Fully modified example below. Best of luck. (and +1 for you're use of remove-erase-idiom for stripping your spaces.)

#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <string>

// helper function for removing extra spaces
void removeSpace( std::string &str )
{
    str.erase( std::remove( str.begin(), str.end(), ' ' ), str.end() );
}

int main()
{
    //===== format is Last Name, First Name, Gender, Color, Birth Date =======//
    std::ofstream os;
    os.open( "test.txt" );

    // first row
    os << " Smith  ," << " John  ," << "   Male , "
    << " Green  , " << " 6  / 7 / 1960  \n";

    // second row
    os << " Mortensen ," << " Mike  ," << " Male  , "
    << " Red  , " << "5/5/  1975 \n";

    // third row
    os << " Johnson ," << " Ann  ," << " Female , "
    << " Blue , " << " 4/ 4 /1985 \n";

    os.close();

    // now let us read data from it
    std::ifstream g;
    g.open( "test.txt" );

    if( g.is_open() )
    {
        std::string line;
        while( std::getline(g, line) )
        {
            std::istringstream iss(line);
            std::string lName, fName, gen, clr, date;
            if (std::getline(iss, lName, ',') &&
                std::getline(iss, fName, ',') &&
                std::getline(iss, gen, ',') &&
                std::getline(iss, clr, ',') &&
                std::getline(iss, date))
            {
                // remove extra spaces from strings
                removeSpace( lName );
                removeSpace( fName );
                removeSpace( gen );
                removeSpace( clr );
                removeSpace( date );

                // display the result
                std::cout << lName
                          << ' ' << fName
                          << ' ' << gen
                          << ' ' << clr
                          << ' ' << date << '\n';
            }
        }
        g.close();
    }

    // since our SSCCE example is done, let us delete the test file
    if( 0 != std::remove( "test.txt" ) )
        std::cout << "Couldn't delete test file!\n\n";
    else
        std::cout << "Successfully deleted test file!\n\n";

    return 0;
}

Output

Smith John Male Green 6/7/1960
Mortensen Mike Male Red 5/5/1975
Johnson Ann Female Blue 4/4/1985
Community
  • 1
  • 1
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • I was just editing my question with a similar code. I wanted to ask you for checking if I missed some I/O error checking... Thank you for introducing me stringstreams and for helping me with this problem. +1 –  May 24 '14 at 11:22
  • @user3261013 I'm very glad it helped. You will rarely find need to manually size `std::string` using most of the library code. When/if you do, chances are you're likely discovered a need to use a `std::vector` with some old-school string manipulation stuff. Save that for another day. Have a good one. – WhozCraig May 24 '14 at 11:56