4

I'm trying to read a text file line by line and extract the data into my program. Essentially, the C++ program will be reading an input file already made that is in this format:

     Republican Senator John McMahon
     Democrat Mayor Steven Markel
     Republican Judge Matt Stevens
     Democrat Senator Anthony Quizitano
     S R
     M D
     J R
     ...
     ..
     ..

The format is basically first 3 lines include the Party, Position, and Name and the next following lines indicate the "results" which are in the form of:

[first letter of position] [party voted for]

So, for example if you see S R, that means 1 vote for a Senator, and it goes to the Republican candidate.

Here's what I have so far:

            #include<fstream>
            int main()
            {
            std::ifstream input("file.txt");
            }

It's my understanding that this will allow me to input the file, and go through it line by line, but I'm not sure how I should proceed to achieve this from here...any help?

Thanks!

sehe
  • 374,641
  • 47
  • 450
  • 633
  • easy way? `input >> var1 >> var2...etc` loop it over the lines if your data is well formatted and you don't have to worry about checking much. Also I'd edit the title. It says C but question is clearly C++ – im so confused Oct 09 '12 at 17:34
  • Are positions always guaranteed to have a unique first letter per party and always be one word? Is the name always guaranteed to be two words? Why have, for example, "S R" when you can determine the party from the position letter? The R is redundant. Are there always going to be 3 candidates? – Joseph Mansfield Oct 09 '12 at 17:37
  • They are guaranteed to be in that format, and there are going to be more candidates. This isn't homework, it's for a project I'm working on myself. – user1732514 Oct 09 '12 at 17:41
  • @user1732514 in that case run a `while()` loop and in it capture your variables in the correct order: `input >> first_letter >> party;` and do with them what you will – im so confused Oct 09 '12 at 17:44
  • This is, of course, after you deal with the first 4 lines (using the same techniques). If you are counting votes, you'd use the data to increment counters, etc... – im so confused Oct 09 '12 at 17:45
  • 2
    @AK4749 You shouldn't loop `while(!input.eof())`. See [here](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) for more info. – Rapptz Oct 09 '12 at 17:47
  • @Rapptz Excellent point! I hadn't considered this. bad advice on my part – im so confused Oct 09 '12 at 17:49
  • So what should I loop to start it off? I know how to create the program in my mind pretty well save for the first opening statements, which is all i need help with – user1732514 Oct 09 '12 at 17:52
  • "*first 3 lines include the Party, Position, and Name*" - But your example has four such lines. – Robᵩ Oct 11 '12 at 01:51

1 Answers1

2

Here is, for fun and glory, an implementation based on Boost Spirit. I added more fake vote input, just so there could be something to display.

  • I wasn't sure whether there is an 1:1 relation between candidates and votes (I'm not a US citizen, and I don't know whether the candidates listed would be voting or being voted for). So I decided to just use fake data.

    const std::string input = 
        "Republican Senator John McMahon\n"
        "Democrat Senator Anthony Quizitano\n"
        "S R\n"
        "S R\n"
        "S R\n"
        "Democrat Mayor Steven Markel\n"
        "Republican Judge Matt Stevens\n"
        "M D\n"
        "J R\n"
        "S R\n"
        "S R\n";
    

    The code however can be used for both purposes.

  • I made it unimportant in what order the input appears.
  • Optionally you can assert, though, that the single letters (S,M,J) actually correspond to positions listed before that point. Enable this by uncommenting the check with posletter_check

See the demo live on http://liveworkspace.org/code/d9e39c19674fbf7b2419ff88a642dc38

#define BOOST_SPIRIT_USE_PHOENIX_V3
#define BOOST_RESULT_OF_USE_DECLTYPE
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>

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

struct Candidate { std::string party, position, name; };

BOOST_FUSION_ADAPT_STRUCT(Candidate, (std::string, party)(std::string, position)(std::string, name))

typedef std::map<std::pair<char, char>, size_t> Votes;
typedef std::vector<Candidate> Candidates;

template <typename It>
    struct parser : qi::grammar<It>
{
    mutable Votes _votes;
    mutable Candidates _candidates;

    parser() : parser::base_type(start)
    {
        using namespace qi;
        using phx::bind; using phx::ref; using phx::val;

        start = (line % eol) >> *eol >> eoi;

        line = 
              vote      [ phx::bind(&parser::register_vote, phx::ref(*this), _1) ]
            | candidate [ phx::push_back(phx::ref(_candidates), _1) ]
            ;

        vote %= graph   
                        // Comment the following line to accept any single
                        // letter, even if a matching position wasn't seen
                        // before:
                        [ _pass = phx::bind(&parser::posletter_check, phx::ref(*this), _1) ]  
            >> ' ' 
            >> char_("RD")
            ;

        candidate = (string("Republican") | string("Democrat"))
            >> ' ' 
            >> as_string [ +graph ]
            >> ' ' 
            >> as_string [ +(char_ - eol) ]
            ;
    }

  private:
    bool posletter_check(char posletter) const
    {
        for (auto& c : _candidates)
            if (posletter == c.position[0])
                return true;
        return false;
    }
    void register_vote(Votes::key_type const& key) const
    {
        auto it = _votes.find(key);
        if (_votes.end()==it)
            _votes[key] = 1;
        else
            it->second++;
    }

    qi::rule<It, Votes::key_type()> vote;
    qi::rule<It, Candidate()> candidate;
    qi::rule<It> start, line;
};

int main()
{
    const std::string input = 
        "Republican Senator John McMahon\n"
        "Democrat Senator Anthony Quizitano\n"
        "S R\n"
        "S R\n"
        "S R\n"
        "Democrat Mayor Steven Markel\n"
        "Republican Judge Matt Stevens\n"
        "M D\n"
        "J R\n"
        "S R\n"
        "S R\n";

    std::string::const_iterator f(std::begin(input)), l(std::end(input));

    parser<std::string::const_iterator> p;

    try
    {
        bool ok = qi::parse(f,l,p);
        if (ok)   
        {
            std::cout << "\ncandidate list\n";
            std::cout << "------------------------------------------------\n";
            for (auto& c : p._candidates)
                std::cout << std::setw(20) << c.name << " (" << c.position << " for the " << c.party << "s)\n";

            std::cout << "\nVote distribution:\n";
            std::cout << "------------------------------------------------\n";
            for (auto& v : p._votes)
                std::cout << '(' << v.first.first << "," << v.first.second << "): " << v.second << " votes " << std::string(v.second, '*') << "\n";
        }
        else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";

        if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
    } catch(const qi::expectation_failure<std::string::const_iterator>& e)
    {
        std::string frag(e.first, e.last);
        std::cerr << e.what() << "'" << frag << "'\n";
    }
}

Output:

candidate list
------------------------------------------------
        John McMahon (Senator for the Republicans)
   Anthony Quizitano (Senator for the Democrats)
       Steven Markel (Mayor for the Democrats)
        Matt Stevens (Judge for the Republicans)

Vote distribution:
------------------------------------------------
(J,R): 1 votes *
(M,D): 1 votes *
(S,R): 5 votes *****
sehe
  • 374,641
  • 47
  • 450
  • 633