C++ 解析输入

标签 c++ c

我需要解析一个看起来像这样的 C++ 标准输入:

N M(对)

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)

如果 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] << ")";   
    }
}

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

最佳答案

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

处理此类错误的 “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";
  }
}

不使用用户定义的类型

解析数据和检查错误的直接方法是使用如下内容,但使用用户定义对象和运算符重载可以大大改进。 p>

  1. read each line using std::getline
  2. construct n std::istringstream iss (line) with the line read
  3. try to read two ints using iss >> N >> M
  4. read M "words" using a std::string s1* with iss >> s1;
    1. Construct a std::istringstream inner_iss using the s1 as initializer
    2. peek to see that the next char available is ( && ignore this char
    3. read integer
    4. peek to see that the next char available is , && ignore this char
    5. read integer
    6. peek to see that the next char available is ) && ignore this char

如果字符串流在第 4 步之后不为空,或者 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

关于C++ 解析输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11541425/

相关文章:

c++ - 如何从 std::optional<T> 移动

c - 错误函数未正确执行

c++ - 如何调用不同命名空间中的函数?

c++ - C++ 11条件变量语义

c - 使用 memset 初始化三值结构数组

c - 如何在数组中进行搜索?

c - C语言对字符串和文件的快速排序

c - 标记化数组 : signal SIGABRT error 上的 realloc()

具有任意类型值的 C++ 关联数组

c++ - 监视标准容器大小调整