Question

Hi I have I some lines of input in the order of:

Date, Time, Price, Volume, Value,

Date is in the format of DD/MM/YY Time is in the format of HH/MM/SS AM/PM and Price, Volume and Value are numbers separated by a comma.

There are 4000 lines of this input and occasionally a share code such as 'CX' or 'NXXT' will appear after the 'value' comma.

My program can't handle this and crashes.

What I need is a way to ignore anything past the comma after 'value' and continue reading the next line. This would be in the 'Shares' class.

Here are the input streams from my classes:

Class: 'Date'

istream & operator >> (istream & input, Date & C) /// An input stream for the day, month and year
{
char delim;
input >> C.day >> delim >> C.month >> delim >> C.year;

return input;   /// Returning the input value
}

Class 'Time'

istream & operator >> (istream & input, Time & C) /// An input stream for the hour minutes and seconds
{
char delim;
input >> C.hour >> delim >> C.minute >> delim >> C.second;
getline(input,C.ampm,',');

return input;   /// Returning the input value
}

Class 'Shares'

istream & operator >> (istream & input, Shares & C) /// An input stream for the day, month and year
{
char delim;
input >> C.price >> delim >> C.volume >> delim >> C.value >> delim;

return input;   /// Returning the input value
}
Was it helpful?

Solution

It looks like Shares is a data structure which represents the different fields. Write a function which parses the lines in a more robust way. Ideally, if Shares represents the data for one single line, just have it take in one single line of your input string. The object doesn't need to be aware even that other lines exist.

In any case don't just write a stream function like this, it is far not robust enough. std::getline will let you split lines on a separator. Of course even with ´std::getline´ you are best still to do proper validation. And you can test it with all sorts of input, for example you could give Shares a method to reconstruct the line from the fields and then have a unit test compare that with the input, to verify it is the same.

Take a look at this function from torset, which parses lines from the tor consensus file, and extracts just the ip address and the ports. It stores the resulting set in a data member std::string _set;, because here it is not the purpose to keep all the fields in a data structure. Note that this function doesn't do validation as it assumes the tor consensus file is correctly formed. In principle that is a dangerous assumption, and ideally you would never run this in production:

IpsetRestore::IpsetRestore( const std::stringstream& consensusIn, const std::string& setName )

:   consensus    ( consensusIn.str() )
  , setName      ( setName           )
  , _errorCode   ( 0                 )

{
    std::string              line      ;
    std::vector<std::string> fields    ;
    std::string              field     ;
    std::stringstream        lineStream;

    // get each line separately
    //
    while( std::getline( consensus, line ) )
    {
        fields    .clear();
        lineStream.clear();
        lineStream.str  ( line );


        // get each field
        //
        while( std::getline( lineStream, field, ' ' ) )

            fields.push_back( std::string( field ) );


        // only work on lines that interest us
        // sample: "r Unnamed VLNV4cpI/C4jFPVQcWKBtO2EBV8 2013-11-04 22:38:31 76.100.70.54 9001 9030"
        //
        if( fields.size() != 8 || fields[ 0 ] != "r" )

            continue;


        // write add lines in the ipset format
        // add [setName] [ip]:[port]
        // tor uses tcp and ipset defaults to tcp, so we won't put it in
        // fields 6 and 7 are the port fields, so if it's port 0, don't bother
        //
        for( int i = 6; i <= 7; ++i )
        {
            if( fields[ i ] == "0" )

                continue;


            _set.append
            (
                std::string( "add "      )
                .append    ( setName     )
                .append    ( " "         )
                .append    ( fields[ 5 ] )
                .append    ( ":"         )
                .append    ( fields[ i ] )
                .append    ( " -exist\n" )
            );
        }



    if( _set.empty() )
    {
        std::cerr << "Something went wrong, _set is empty. Maybe you passed the wrong inputfile or it was not formatted correctly." << std::endl;

        ++_errorCode;
    }
}

OTHER TIPS

Write a function that skips until and including the end of the line.

void skipEOL(std::istream& in)
{
  int c;
  while ( (c = in.getc()) != '\n' && c != EOF );
}

Use it when you know you need to skip everything until and including the end of the line.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top