C++解析输入

Ext*_*Ext 1 c c++

我需要解析一个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)

我的问题是,最好/更正确的方法是什么?

Fil*_*efp 6

由于永远不能信任到应用程序的输入数据,因此添加错误检查以查看提供的数据确实有效(否则应用程序的结果在解析时可能会出错)是非常重要的.

处理诸如此类错误的"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)

不使用用户定义的类型

解析数据以及检查错误的直接方法是使用以下内容,尽管可以通过使用用户定义对象和运算符重载来大大改进.

  1. 使用std :: getline读取每一行
  2. 使用行读取构造n std :: istringstream iss(行)
  3. 尝试使用iss >> N >> M读取两个整数
  4. 使用带有iss >> s1的std :: string s1*读取M个 "单词" ;
    1. 使用s1作为初始化程序构造std :: istringstream inner_iss
    2. 看看下一个可用的字符是(&&忽略这个字符
    3. 读整数
    4. 看看下一个可用的字符是,&&忽略这个字符
    5. 读整数
    6. 看看下一个可用的字符是)&&忽略这个字符

如果在步骤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)