由于应用程序的输入数据永远不可信,因此添加错误检查以查看所提供的数据确实有效(否则应用程序的结果可能会在解析时出现错误)非常重要。
处理此类错误的“C++ 方式”是在负责解析数据的函数中出现问题时抛出异常。
然后,此函数的调用者将调用包装在try-catch-block中以捕获可能出现的错误。
使用用户定义类型..
定义自己的类型来保存数据对将大大提高代码的可读性,以下实现的输出与本文后面的实现是相同的。
#include <iostream>
#include <string>
#include <sstream>
#include <stdexcept>
struct Pair {
Pair (int a, int b)
: value1 (a), value2 (b)
{}
static Pair read_from (std::istream& s) {
int value1, value2;
if ((s >> std::ws).peek () != '(' || !s.ignore () || !(s >> value1))
throw std::runtime_error ("unexpected tokens; expected -> (, <value1>");
if ((s >> std::ws).peek () != ',' || !s.ignore () || !(s >> value2))
throw std::runtime_error ("unexpected tokens; expected -> , <value2>");
if ((s >> std::ws).peek () != ')' || !s.ignore ())
throw std::runtime_error ("unexpected token;expected -> )");
return Pair (value1,value2);
}
int value1, value2;
};
我注意到程序员可能难以理解上述内容的一件事是使用s >> std::ws
; 它用于消耗可用的空白,以便我们可以使用.peek
来获取下一个可用的非空白字符。
我实现静态函数的原因是后者将要求我们在从流中读取之前创建一个对象,这在某些情况下是不可取的read_from
。ostream& operator>>(ostream&, Pair&)
void
parse_data () {
std::string line;
while (std::getline (std::cin, line)) {
std::istringstream iss (line);
int N, M;
if (!(iss >> N >> M))
throw "unable to read N or M";
else
std::cerr << "N = " << N << ", M = " << M << "\n";
for (int i =0; i < M; ++i) {
Pair data = Pair::read_from (iss);
std::cerr << "\tvalue1 = " << data.value1 << ", ";
std::cerr << "\tvalue2 = " << data.value2 << "\n";
}
}
}
通常我不建议只用大写命名非常量变量,但为了更清楚地说明哪个变量包含我使用与输入描述相同的名称。
int
main (int argc, char *argv[])
{
try {
parse_data ();
} catch (std::exception& e) {
std::cerr << e.what () << "\n";
}
}
不使用用户定义类型
解析数据以及检查错误的直接方法是使用以下内容,尽管使用用户定义的对象和运算符重载可以大大改进它。
- 使用std::getline读取每一行
- 构造 n std::istringstream iss (line)并读取该行
- 尝试使用iss >> N >> M读取两个整数
- 使用带有iss >> s1的 std::string s1*读取M个 “单词” ;
- 使用s1作为初始化器构造一个std::istringstream inner_iss
- 看看下一个可用的字符是
(
&& 忽略这个字符
- 读取整数
- 看看下一个可用的字符是
,
&& 忽略这个字符
- 读取整数
- 看看下一个可用的字符是
)
&& 忽略这个字符
如果在第 4 步之后 stringstream 不为空,或者iss.good ()在步骤之间的任何位置返回 false,则说明读取的数据存在语法错误。
示例实现
可以通过以下链接找到源代码(代码放在别处以节省空间):
N = 0, M = 0
N = 2, M = 1
value1 = 0, value2 = 1
N = 2, M = 0
N = 5, M = 8
value1 = 0, value2 = 1
value1 = 1, value2 = 3
value1 = 2, value2 = 3
value1 = 0, value2 = 2
value1 = 0, value2 = 1
value1 = 2, value2 = 3
value1 = 2, value2 = 4
value1 = 2, value2 = 4