1

I am trying to create a C++ code that using boost libraries reads an input file like the following,

    1             12       13        0        0      1      0      INLE
    .
    .
    .

In this case, I must do an action if the condition specified on the last column of the right is INLE. I have the following code,

#include <iostream>
#include <fstream>
#include <string>
#include <boost/algorithm/string/predicate.hpp>


int main(int argc, const char * argv[])
{
    std::string line;
    const std::string B_condition = "INLE";
    std::ifstream myfile ("ramp.bnd");
    if (myfile.is_open())
    {
        while ( getline (myfile,line) )
        {
            if (boost::algorithm::ends_with(line,B_condition)==true)
            {
                std::cout << "Its True! \n"; // just for testing
                //add complete code
            }
        }
        myfile.close();
    }

    else std::cout << "Unable to open file";

    return 0;
}

while compiling there are no issues, but when I run, it doesnt shows anything.

By the other side, if I modify my boolean condition to false, it will print "Its true!" the number of lines that my input file has.

What am I doing wrong? Thanks!!

heri-salmas
  • 103
  • 2
  • 12

2 Answers2

2

I can only assume that:

  • your file contains whitespace at the end (use trim)
  • your file has windows line ends (CRLF) but you're reading it as UNIX text files, meaning that the lines will include a trailing `\r' (CR) (often shown as ^M in various text editors/pagers).

So, either

  • fix the line endings
  • trim whitespace from the lines before comparing
  • or both

Best: use a 'proper' parser to do the work.

Update adding a quick & dirty approach using Boost Spirit: see it Live On Coliru

int main()
{
    std::ifstream myfile("ramp.bnd");
    myfile.unsetf(std::ios::skipws);

    boost::spirit::istream_iterator f(myfile), l;

    using namespace qi;
    bool ok = phrase_parse(f, l,
            (repeat(7) [ int_ ] >> as_string[lexeme[+(char_ - eol)]])
                [ phx::bind(process_line, _1, _2) ]
            % eol, // supports CRLF and LF
            blank);

    if (!ok)
        std::cerr << "Parse errors\n";
    if (f!=l)
        std::cerr << "Remaing input: '" << std::string(f,l) << "'\n";
}

As you can see, it validates the whole line, assuming (for now) that the columns are 7 integer values and a string (e.g. "INLE"). Now, the actual work is much simpler and can be implemented in a separate function:

void process_line(std::vector<int> const& values, std::string const& kind)
{
    if (kind == "INLE")
    {
        std::cout << "Column 1: " << values[0] << "\n";
    }
}

The actual processing function doesn't have to meddle with trimming, line ends, even parsing the details columns :)

Full Code for reference

#include <iostream>
#include <fstream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi  = boost::spirit::qi;
namespace phx = boost::phoenix;

static const std::string B_condition = "INLE";

void process_line(std::vector<int> const& values, std::string const& kind)
{
    if (kind == "INLE")
    {
        std::cout << "Column 1: " << values[0] << "\n";
    }
}

int main()
{
    std::ifstream myfile("ramp.bnd");
    myfile.unsetf(std::ios::skipws);

    boost::spirit::istream_iterator f(myfile), l;

    using namespace qi;
    bool ok = phrase_parse(f, l,
            (repeat(7) [ int_ ] >> as_string[lexeme[+(char_ - eol)]])
                [ phx::bind(process_line, _1, _2) ]
            % eol, // supports CRLF and LF
            blank);

    if (!ok)
        std::cerr << "Parse errors\n";
    if (f!=l)
        std::cerr << "Remaing input: '" << std::string(f,l) << "'\n";
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks @sehe that is an amazing approach! Now I will study your code and modify it to match my requirements! Thanks!!! – heri-salmas Mar 19 '14 at 10:56
  • @heri-salmas have you figured at what the problem was ? (my money is on the line-endings :)) – sehe Mar 19 '14 at 11:03
  • I think its a great idea, but for a problem that simple at the end I felt like killing flies with atomic bombs. I ended up doing a simpler approach! Thanks anyhow!! – heri-salmas Mar 19 '14 at 11:58
  • (Did you ever find out what the problem was? I tested your original code with the same input and it worked.) – sehe Mar 19 '14 at 12:18
  • 1
    @heri-salmas My point is that my answer was in the first four lines. I'm convinced ***this*** solved your problem **and** the question as posed. I'm sorry that I also showed something that you were not interested in, I guess :( – sehe Mar 19 '14 at 12:55
  • I think that you were right and the problem was in the line endings, at least while compiling in Windows i didnt had problems. (the input file was created in windows.) Your method was really interesting and really helpful, I will definitely use it, but not for this little project! :) :) – heri-salmas Mar 19 '14 at 14:35
  • @heri-salmas You do realize that you are usually expected to accept the answer that solved your issue, right? [see here](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) My answer was ***not*** about that sample code. – sehe Mar 19 '14 at 14:38
  • You are completely right! I just corrected the mistake ;) @sehe Thanks for your help and advices! – heri-salmas Mar 19 '14 at 14:42
  • @heri-salmas Cheers. I +1-ed the other answer for mentioning the bare-bones approach to general parsing – sehe Mar 19 '14 at 14:44
2

You don't need a library like boost at all. A solution with pur standard C++ is possible in some lines of code too:

const std::string B_condition = "INLE";
std::ifstream myfile ("ramp.bnd");

for( char c; myfile >> c; )
{
    if( std::isdigit(c, myfile.getloc() ) ) // needs #include <locale>
    {
        int i;
        if( myfile.putback(c) >> i )
            std::cout << "read " << i << std::endl; // do something with 'i'
    }
    else
    {
        std::string token;
        if( myfile.putback(c) >> token )
        {
            if( token == B_condition )
                std::cout << B_condition << " found\n";
            else
                ; // no number, no B_condition -> what ever You want to do
        }
    }
}
cpp-progger
  • 406
  • 3
  • 6
  • 2
    I think OP's original code is superior to this in many ways. There is no explicit whitespace recognition possible here. This doesn't do any input validation either. It has no concept of line separations. Also, it will match a line contains `INLE` in the first column just as happily (and there would be no way to get at the rest of the line there unless you made the rest of parser build state). I'd vouch for the OP's original code with the whitespace/line-end issues fixed. This has **[nothing to do with requiring boost](http://coliru.stacked-crooked.com/a/e9e736518a3e643a)** /cc @heri-salmas – sehe Mar 19 '14 at 13:13
  • +1 for effort and showing that many things can be done with judicious composition of standard library features (even though that comes with it's own cost trade-offs) – sehe Mar 19 '14 at 14:44