简体   繁体   English

在C ++中解析逗号分隔的数字

[英]Parsing comma-delimited numbers in C++

I have a quick question for everyone. 我对每个人都有一个简单的问题。 I'm trying to write a simple code to extract numbers form user input and save them to an int array, but I'm having a hard time wrapping my mind around how to make it work. 我试图编写一个简单的代码以从用户输入中提取数字并将其保存到int数组中,但是我很难确定如何使它工作。 The code shown below works well for single-digit numbers, but not so much for numbers with more than 1 digit. 下面显示的代码适用于一位数字,但不适用于超过1位的数字。

For instance, if user enters: 1,2,3,4,50,60 here is what I get: 例如,如果用户输入:1,2,3,4,50,60,这就是我得到的:

Enter numbers (must be comma delimited): 1,2,3,4,50,60
My numbers are: 12345060
My parsed numbers are: 1
My parsed numbers are: 2
My parsed numbers are: 3
My parsed numbers are: 4
My parsed numbers are: 5
My parsed numbers are: 0

Question: how can I modify this simple piece of code to accurately capture numbers with more than 1 digit? 问题:如何修改这段简单的代码以准确捕获超过1位的数字? Thanks in advance!! 提前致谢!!

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
using namespace std;


// set up some variables
int numbers[100];


int main() {

// Enter numbers (comma delimited). Ex: 1,2,3,4,50,60<return>
cout << endl << endl << "Enter numbers (must be comma delimited): ";

string nums_in;
getline(cin, nums_in);
nums_in.erase(remove(nums_in.begin(), nums_in.end(), ','), nums_in.end());  // remove the unwanted commas

cout << "My numbers are: " << nums_in << endl;


// convert each char into int
for (int o = 0; o < 6; o++) {
    istringstream buf(nums_in.substr(o,1));
    buf >> numbers[o];
    cout << "My parsed numbers are: " << numbers[o] << endl;
}
cout << endl << endl;

cout << "Done." << endl;
return 0;

}

To solve this kind of problems, you have to write a scanner . 要解决此类问题,您必须编写扫描仪 The scanner breaks input into tokens . 扫描程序将输入分为令牌 Once you have the ability to break the input into tokens, you can check the order of tokens (see parsers ). 一旦能够将输入分解为标记,就可以检查标记的顺序(请参阅解析器 )。

In your case you have three tokens: number , comma and end . 在您的情况下,您有三个标记: numbercommaend An example of valid input: number comma number end . 有效输入的示例: number comma number end Another example: end (empty input). 另一个示例: end (空输入)。 An example of invalid input: number number end (there is no comma between numbers). 输入无效的示例: number number end (数字之间没有逗号)。

Below it is a possible solution to your problem. 下面是解决您的问题的可能方法。 get_token reads a token from input and stores it in token and number globals. get_token从输入中读取令牌,并将其存储在token和全局number get_numbers reads tokens, checks the syntax and stores the numbers in numbers ; get_numbers读取令牌,检查语法和在存储的数字numbers ; the count of numbers is stored in count (also global variables). 数字的计数存储在count (也是全局变量)中。

#include <iostream>
#include <cctype>

enum { max_count = 100 };
int numbers[max_count];
int count;

enum token_type
{
  token_unknwon,
  token_end,
  token_comma,
  token_number
};

token_type token;
int number;

token_type get_token()
{
  char c;

  // get a character, but skip ws except newline
  while ( std::cin.get( c ) && c != '\n' && std::isspace( c ) )
    ;
  if ( ! std::cin || c == '\n' )
    return token = token_end;

  if ( c == ',' )
    return token = token_comma;

  if ( std::isdigit( c ) )
  {
    std::cin.unget();
    std::cin >> number;
    return token = token_number;
  }

  return token_unknwon;
}

enum error_type
{
  error_ok,
  error_number_expected,
  error_too_many_numbers,
  error_comma_expected
};

int get_numbers()
{
  // 
  if ( get_token() == token_end )
    return error_ok; // empty input

  while ( true )
  {
    // number expected
    if ( token != token_number )
      return error_number_expected;

    // store the number
    if ( count >= max_count )
      return error_too_many_numbers;
    numbers[count++] = number;

    // this might be the last number
    if ( get_token() == token_end )
      return error_ok;

    // not the last number, comma expected
    if ( token != token_comma )
      return error_comma_expected;

    // prepare next token
    get_token();
  }

}

int main()
{
  //...
  switch ( get_numbers() )
  {
  case error_ok: break;
  case error_comma_expected: std::cout << "comma expected"; return -1;
  case error_number_expected: std::cout << "number expected"; return -2;
  case error_too_many_numbers: std::cout << "too many numbers"; return -3;
  }

  //
  std::cout << count << " number(s): ";
  for ( int i = 0; i < count; ++i )
    std::cout << numbers[i] << ' ';
  //...
  return 0;
}

This task can be easily done using std::getline to read the entire line in a string and then parse that string using a std::istringstream to extract the individual numbers and skip the commas. 使用std :: getline读取字符串中的整行,然后使用std :: istringstream解析该字符串以提取单个数字并跳过逗号,可以轻松完成此任务。

#include <iostream>
#include <sstream>
#include <vector>

using std::cout;

int main() {   
    // Enter numbers (comma delimited). Ex: 1,2,3,4,50,60<return>
    cout << "\nEnter numbers (must be comma delimited): ";

    int x;
    std::vector<int> v;
    std::string str_in;

    // read the whole line then use a stringstream buffer to extract the numbers
    std::getline(std::cin, str_in);
    std::istringstream str_buf{str_in};

    while ( str_buf >> x ) {
        v.push_back(x);
        // If the next char in input is a comma, extract it. std::ws discards whitespace
        if ( ( str_buf >> std::ws).peek() == ',' ) 
            str_buf.ignore();
    }

    cout << "\nMy parsed numbers are:\n";
    for ( int i : v ) {
        cout << i << '\n';
    }

    cout << "\nDone.\n";

    return 0;

}

In your program, you first remove the "unwanted" commas in the input string and then run into the problem that you cannot distinguish the numbers in the input line any more. 在您的程序中,首先删除输入字符串中的“不需要的”逗号,然后遇到无法再区分输入行中数字的问题。 So it seems as if these commas are not unwanted after all. 因此,似乎这些逗号毕竟不是多余的。 The solution is to parse the string step by step without removing the commas first, as you need them to split up the input string. 解决方案是分步解析字符串,而不先删除逗号,因为您需要使用逗号分割输入字符串。 Here is an example. 这是一个例子。

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <vector>

int main() {

    // Enter numbers (comma delimited). Ex: 1,2,3,4,50,60<return>
    std::cout << std::endl << std::endl << "Enter numbers (must be comma delimited): ";
    std::string nums_in;
    std::getline(std::cin, nums_in);

    // Now parse
    std::vector<int> data;
    std::istringstream buf(nums_in);
    while (!buf.eof()) {
        int this_number;
        buf >> this_number;
        if (buf.bad()) {
            std::cerr << "Number formatting error!\n";
            return 1;
        }
        data.push_back(this_number);
        char comma = 0;
        buf >> comma;
        if (!buf.eof()) {
            if (buf.fail()) {
                std::cerr << "Could not read comma.\n";
                return 1;
            }
            if (comma!=',') {
                std::cerr << "Found no comma but '" << comma << "' instead !\n";
                return 1;
            }
        }
    }

    std::cout << "My numbers are:";
    for (auto a : data) {
        std::cout << " " << a;
    }
    std::cout << std::endl;

    std::cout << "Done." << std::endl;
    return 0;
}

Note that I did not use "using namespace std;" 请注意,我没有使用“使用命名空间std;”。 as it is considered to be bad style. 因为它被认为是不好的风格。 Also, I used a C++11 feature for printing out the values and used a vector to store the numbers - in your code, typing in a line with 200 numbers would lead to a crash (or other bad behavior). 另外,我使用了C ++ 11功能来打印出值,并使用了向量来存储数字-在您的代码中,键入200个数字的行会导致崩溃(或其他不良行为)。 Finally, the parsing error handling is not yet complete. 最后,解析错误处理尚未完成。 Making it complete and correct is left as an exercise. 使其完整和正确是一项练习。 An alternative to the istringstream-based approach would be to first split the line by the commas and then to read all numbers separately using istringstreams. 基于istringstream的方法的替代方法是先用逗号分隔行,然后使用istringstreams分别读取所有数字。

By the way, your question is so practical that it would have been better suited for the standard stackexchange site - the connection to computer science is quite weak. 顺便说一句,您的问题如此实用,以至于它更适合标准的stackexchange网站-与计算机科学的联系非常薄弱。

Hm... How about parsing the string without removing the commas? 嗯...在不删除逗号的情况下解析字符串呢? Read the string character for character, placing each character in a temp buffer until you hit a comma, then convert the temp buffer to an int and store it in the vector. 读取字符的字符串字符,将每个字符放置在临时缓冲区中,直到遇到逗号为止,然后将临时缓冲区转换为int并将其存储在向量中。 Empty the temp buffer and repeat. 清空临时缓冲区并重复。

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <sstream>
using namespace std;


// set up some variables
vector<int> numbers(0);


int main() {

// Enter numbers (comma delimited). Ex: 1,2,3,4,50,60<return>
cout << endl << endl << "Enter numbers (must be comma delimited): ";

string nums_in;
getline(cin, nums_in);

cout << "My numbers are: " << nums_in << endl;

string s_tmp = "";
int i_tmp;

for(vector<int>::size_type i = 0, len = nums_in.size(); i < len; i++){
    if( nums_in[i] == ',' ){
        if(s_tmp.size() > 0){
            i_tmp = std::stoi(s_tmp);
            numbers.push_back(i_tmp);
        }
        s_tmp = "";
    }
    else if( i == len-1){
        s_tmp += nums_in[i];
        i_tmp = std::stoi(s_tmp);
        numbers.push_back(i_tmp);
        cout << "My parsed numbers are:" << i_tmp << endl;
    }
    else {
        s_tmp += nums_in[i];
    }
}

cout << endl << endl;

cout << "Done." << endl;
return 0;

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM