我有一个从用户那里获取命令的程序,它将以不同的方式处理不同的命令。 例如:
ADD_STUDENT ALEX 5.11 175
ADD_TEACHER MERY 5.4 120 70000
PRINT MERY
REMOVE ALEX
PRINT TEACHER SALARY
PRINTALL
因此,我需要检查每一行,看看输入是由什么组成的。
这是我的代码,但我想我误解了问题的方式<<工作。 有人可以给我一个建议吗?并告诉我为什么我的代码不能像我预期的那样工作?
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...
}
我的想法是发布>>第一个>>第二个>>如果填充了所有参数,则第三个将返回true 如果没有足够的论据,则为假。但显然我错了。
答案 0 :(得分:10)
你的问题很糟糕。这总是促使我使用Boost Spirit提供夸大的示例实现。
注意:请不要将其作为您的家庭作业分配。
使用以下示例输入查看 Live on 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
完整代码:
更新如果显示 here ,请加入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);
};
请参阅改编示例 Live on 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
答案 1 :(得分:7)
这样做:
iss >> command;
if (!iss)
cout << "error: can not read command\n";
else if (command == "ADD_STUDENT")
iss >> name >> height >> weight;
else if (command == "ADD_TEACHER")
iss >> name >> height >> weight >> salary;
else if ...
答案 2 :(得分:5)
您的问题是使用>>
运算符会从流中读取并清除令牌。
if(iss >> command >> name >> height >> weight)
这(上图)尝试从流中读取4个令牌,并且对于每次成功读取,它都会清除流中的读取数据。
else if(iss >> command >> name >> height >> weight >> salary)
当你到达这个(上面)时,这意味着某些令牌无法被读取并转换为适当的类型,但是可能至少已经从流中剥离了命令令牌。
答案 3 :(得分:2)
嗯,在upvotes有太多机会已经太晚了,但你们让我想到这个......
为了实现稳健性,您可以将解析分为两个阶段:第一阶段获取行,第二阶段占用一行并对其执行某些操作。
对于第一阶段,您可以使用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);
}
}
第二阶段处理命令。它可能像这样直接:
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...
)
没有测试任何这个,但它应该主要在那里。请注意评论中的任何错误。
P.S。这非常低效并且运行速度比正确设计的命令解析器慢,但是对于大多数作业来说它应该足够好了。
答案 4 :(得分:1)
你可以在技术上对整个输入行进行标记,但这似乎与你的关卡相差太远了。如果您确实想要进入它,那么有一个很好的页面和教程here将帮助您使用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