6

ユーザーからコマンドを受け取るプログラムがあり、さまざまなコマンドを異なる方法で処理します。例えば:

ADD_STUDENT ALEX 5.11 175
ADD_TEACHER MERY 5.4  120 70000
PRINT MERY 
REMOVE ALEX
PRINT TEACHER SALARY
PRINTALL 

したがって、各行を調べて、入力が何で構成されているかを確認する必要があります。

これが私のコードですが、 iss<< の仕組みを誤解していると思います。誰かが私に提案をしてもらえますか? また、コードが期待どおりに機能しなかった理由を教えてください。

string line;
while(getline(cin, line))
{
  //some initialization of string, float variable
  std::istringstream iss(line);
  if(iss >> command >> name >> height >> weight)
   ..examine the command is correct(ADD_STUDENT) and then do something..
  else if(iss >> command >> name >> height >> weight >> salary)
   ..examine the command is correct(ADD_TEACHER) and then do something...
  else if(iss >> command >> name)
   ..examine the command is correct(REMOVE) and then do somethin...
}

私の考えでは、 is>> first >>second >> third は、すべての引数が満たされている場合は true を返し、十分な引数がない場合は false を返します。しかし、明らかに私は間違っています。

4

5 に答える 5

10

あなたの問題はうまく指定されていませんでした。これにより、Boost Spirit を使用した大げさな実装例を提供するように常に促されます。

:これを宿題として提出しないでください。

次のサンプル入力を使用して、Coliruでライブを参照してください。

ADD_STUDENT ALEX 5.11 175
ADD_STUDENT PUFF 6 7
ADD_STUDENT MAGIC 7 8
ADD_STUDENT DRAGON 8 9
ADD_TEACHER MERY 5.4  120 70000
PRINT MERY 
ADD_TEACHER DUPLO 5.4  120 140000
PRINTALL  10
REMOVE ALEX
PRINT  TEACHER SALARY
PRINT  MERY PUFF MAGIC DRAGON
REMOVE MERY PUFF MAGIC DRAGON
PRINT  TEACHER SALARY

完全なコード:


更新ここmake_visitor.hppに示すように含めると、ビジター コードをよりエレガントに記述できます。

auto print_salary = [&] () 
{ 
    for(auto& p : names) 
        boost::apply_visitor(make_visitor(
                    [](Teacher const& v) { std::cout << "Teacher salary: " << v.salary << "\n"; },
                    [](Student const& v) {}), 
                p.second);
};

適応例を見るColiruでライブ


#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

struct Person
{
    std::string name;
    double height, weight;
    friend std::ostream& operator<<(std::ostream& os, Person const& s) {
        return os << "Person { name:" << s.name << ", height:" << s.height << ", weight:" << s.weight << " }";
    }
};

struct Student : Person
{
    Student() = default;
    Student(std::string n, double h, double w) : Person {n,h,w} {}
};

struct Teacher : Person
{
    Teacher() = default;
    Teacher(std::string n, double h, double w, double s) : Person {n,h,w}, salary(s) {}
    double salary;
};

int main()
{
    std::stringstream ss;
    ss << std::cin.rdbuf();

    std::map<std::string, boost::variant<Student, Teacher> > names;

    using namespace qi;
    auto add_student  = phx::ref(names)[_1] = phx::construct<Student>(_1, _2, _3);
    auto add_teacher  = phx::ref(names)[_1] = phx::construct<Teacher>(_1, _2, _3, _4);
    auto remove       = phx::erase(phx::ref(names), _1);
    auto print_all    = [&] (int i) { for(auto& p : names) { std::cout << p.second << "\n"; if (--i==0) break; } };
    auto print_salary = [&] () 
    { 
        struct _ : boost::static_visitor<> {
            void operator()(Teacher const& v) const { std::cout << "Teacher salary: " << v.salary << "\n"; }
            void operator()(Student const& v) const { }
        } v_;
        for(auto& p : names) boost::apply_visitor(v_, p.second);
    };

    auto name_ = as_string[lexeme[+graph]];

    if (phrase_parse(begin(ss.str()), end(ss.str()), 
                (
                     ("ADD_STUDENT" >> name_ >> double_ >> double_)            [ add_student ]
                   | ("ADD_TEACHER" >> name_ >> double_ >> double_ >> double_) [ add_teacher ]
                   | (eps >> "PRINT" >> "TEACHER" >> "SALARY")                 [ print_salary ]
                   | ("PRINTALL" >> int_)      [ phx::bind(print_all, _1) ]
                   | ("PRINT"  >> +name_       [ std::cout << phx::ref(names)[_1] << std::endl ])
                   | ("REMOVE" >> +name_       [ remove ])
                ) % +eol,
                qi::blank))
    {
        std::cout << "Success";
    }
    else
    {
        std::cout << "Parse failure";
    }
}

出力:

Person { name:MERY, height:5.4, weight:120 }
Person { name:ALEX, height:5.11, weight:175 }
Person { name:DRAGON, height:8, weight:9 }
Person { name:DUPLO, height:5.4, weight:120 }
Person { name:MAGIC, height:7, weight:8 }
Person { name:MERY, height:5.4, weight:120 }
Person { name:PUFF, height:6, weight:7 }
Teacher salary: 140000
Teacher salary: 70000
Person { name:MERY, height:5.4, weight:120 }
Person { name:PUFF, height:6, weight:7 }
Person { name:MAGIC, height:7, weight:8 }
Person { name:DRAGON, height:8, weight:9 }
Teacher salary: 140000
Success
于 2013-07-21T20:32:26.440 に答える
2

まあ、賛成票を投じるチャンスには遅すぎますが、皆さんは私にこれについて考えさせました...

堅牢性のために、解析を 2 つの段階に分割できます。最初の段階では行を取得し、2 番目の段階では行を取得して処理します。

最初の段階では、次を使用できますgetline

#include <string>
#include <sstream>

void ParseLines(std::istream& source)
{
    while(source)
    {
        // Get a line from the source.
        std::string inputLine;
        std::getline(source, inputLine);

        // Make a stream out of it.
        std::istringstream inputStream(inputLine);
        std::string command;
        inputStream >> command;
        if(inputStream) // Empty or bad line: skip
            HandleCommand(command, inputStream);
    }
}

2 番目のステージは、コマンドを処理します。次のような直接的なものである可能性があります。

void HandleCommand(const std::string& command, std::istringstream& params)
{
    if(command == "ADD_STUDENT")
    {
        float someFloat;
        int someInt;
        params >> someFloat >> someInt;
        // add the student.
    }
    // etc.
}

しかし、私は恥ずかしがらず、ファクトリパラダイムを実装します。

#include <map>

typedef void (*CommandHandler)(const std::string&, std::istringstream&);
typedef std::map<std::string, CommandHandler> CommandTable;

static CommandTable gCommands; // Yep. A global. Refactor however you see fit.

void HandleCommand(const std::string& command, std::istringstream& params)
{
    CommandTable::iterator handler = gCommands.find(command);
    if(handler == gCommands.end())
    {
        // Handle "command not found" error.
        return;
    }

    (*(handler->second))(command, params);
}

void AddStudent(const std::string& command, std::istringstream& params)
{
    float someFloat;
    int someInt;
    params >> someFloat >> someInt;
    // add the student.
}

// Other command handling functions here...

void RegisterCommands()
// Call this once prior to parsing anything,
// usually one of the first things in main().
{
    gCommands["ADD_STUDENT"] = &AddStudent;
    // ... other commands follow...
)

これをテストしたことはありませんが、ほとんどはそこにあるはずです。コメントのバグに注意してください。

PS これは非常に非効率的であり、適切に設計されたコマンド パーサーよりも遅く実行されますが、ほとんどのジョブには十分なはずです。

于 2013-07-21T02:29:41.710 に答える
1

技術的には入力行全体をトークン化することもできますが、それはあなたのレベルから少し離れすぎているようです。どうしても入りたい場合は、strtok() の使用に役立つ素敵なページとチュートリアルがここにあります。

その方法に行きたくない場合は、コマンドのリストを個別に解析できます。「command」という名前の文字列を読み取ったとします。

if (command == "ADD_STUDENT")
{
    int weight, height, otherfield;
    cout << ">" << flush;
    cin >> weight >> height >> otherfield;
    //do something, like add them to the database
}

それはあなたの最善の策のように思えますが、それは多くのコーディングですが、おそらく達成するのがより簡単です. あなたは本当にそれに夢中になり、次のようなフォーマット文字列を使用することができます:

scanf("%s, %s %d, %f", lastname, firstname, age, height);

このようにすると、入力は次のようになります。

ADD_STUDENT Doe, John 30, 5.6
于 2013-07-21T01:55:03.823 に答える