我需要解析一个C++ stdin输入,看起来像这样:
NM(对)
0 0
2 1 (0,1)
2 0
5 8 (0,1) (1,3) (2,3) (0,2) (0,1) (2,3) (2,4) (2,4)
Run Code Online (Sandbox Code Playgroud)
如果N> 0 && M> 0,则接下来是M对.它是单行输入,所以我不知道该怎么做.
我有一些解决方案,但有些东西告诉我这不是最好的解决方案.
void input(){
int a[100][2];
int n,m;
char ch;
cin >> n >> m;
for ( int i = 0; i < m; i++) {
cin >> ch >> a[i][0]>> ch>> a[i][1]>>ch;
}
cout << n << " " << m << " \n";
for ( int i=0; i < m; i++ ) {
cout << "(" << a[i][0] << " ," << a[i][1] << ")";
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,最好/更正确的方法是什么?
由于永远不能信任到应用程序的输入数据,因此添加错误检查以查看提供的数据确实有效(否则应用程序的结果在解析时可能会出错)是非常重要的.
处理诸如此类错误的"C++方式"是在负责解析数据的函数中出现问题时抛出异常.
然后,此函数的调用者将把调用包装在try-catch-block中以捕获可能出现的错误.
定义自己的类型以保存数据对将极大地提高代码的可读性,以下实现的输出和本文后面的内容是相同的.
#include <iostream>
#include <string>
#include <sstream>
#include <stdexcept>
Run Code Online (Sandbox Code Playgroud)
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;
};
Run Code Online (Sandbox Code Playgroud)
我注意到的一件事,程序员可能很难掌握上面的内容是使用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";
}
}
}
Run Code Online (Sandbox Code Playgroud)
通常我不建议仅以大写形式命名非常量变量,但要更清楚地说明哪个变量包含的内容与输入描述的名称相同.
int
main (int argc, char *argv[])
{
try {
parse_data ();
} catch (std::exception& e) {
std::cerr << e.what () << "\n";
}
}
Run Code Online (Sandbox Code Playgroud)
解析数据以及检查错误的直接方法是使用以下内容,尽管可以通过使用用户定义对象和运算符重载来大大改进.
- 使用std :: getline读取每一行
- 使用行读取构造n std :: istringstream iss(行)
- 尝试使用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
Run Code Online (Sandbox Code Playgroud)