总结数字串输入的最佳方法

时间:2017-05-03 15:06:14

标签: c++ arrays stdout

问题

目前我正在查看HackerRank上的一个问题,其输入格式为:

4 
6 7 8 9 

基本上,第一行指定输入整数的数量,第二行指定后面的所有整数。看起来很简单,但不确定为什么我的程序不起作用。

我的解决方案

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <string>
#include <algorithm>

int main(){

    int n;
    std::cin >> n;

    // Get all the numbers 
    std::string rawInput;
    std::cout << "we have reached here 1";
    std::cin >> rawInput;
    std::cout << "we have reached here 2";
    std::vector<std::string> numbers;
    std::string number = ""; 
    for (int i = 0; i < rawInput.size(); i++) {
        char c = rawInput[i];
        if (c == ' ') {
            numbers.push_back(number);
        }
        number += c; 
    }

    // Get all the ints
    int sum = 0;
    std::cout << sum;
    for (int j = 0; j < n; j++) { 
        sum += stoi(numbers[j]); 
    } 

    std::cout << sum; 

    return 0;
}

错误

现在我没有看到调试cout行:std::cout << "we have reached here 2";。我不确定为什么会这样。

调试器输出

DB trace:
Reading symbols from solution...done.
[New LWP 18595]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `solution'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000401042 in __gnu_cxx::__stoa<long, int, char, int> (
    __idx=<optimized out>, __str=<optimized out>, __name=<optimized out>, 
    __convf=<optimized out>) at /usr/include/c++/6/ext/string_conversions.h:68
68        const _TRet __tmp = __convf(__str, &__endptr, __base...);
#0  0x0000000000401042 in __gnu_cxx::__stoa<long, int, char, int> (
    __idx=<optimized out>, __str=<optimized out>, __name=<optimized out>, 
    __convf=<optimized out>) at /usr/include/c++/6/ext/string_conversions.h:68
#1  std::__cxx11::stoi (__base=10, __idx=0x0, __str=...)
    at /usr/include/c++/6/bits/basic_string.h:5414
#2  main () at solution.cc:32

4 个答案:

答案 0 :(得分:4)

崩溃的直接原因是假设n是正确的。您从未在代码中确认过,但在迭代numbers时继续使用它。然后,您将超出numbers并导致stoi爆炸。

事实上,n不是4!它只有1,因为你的第二行输入被打破了。只需打印出rawInput的值,您就会看到。

要阅读6 7 8 9行,您需要std::getline(std::cin, rawInput)。格式化提取到std::string(您现在正在使用)只会提取第一个&#34;令牌&#34 ;;即,只是6

但是,当切换到std::getline时,你 现在需要在拓扑排序的答案中探索的换行符 - 跳过滑稽动作,因为非格式化提取不会跳过空格同样的方式。

最后,在将其添加到矢量后,您永远不会清除number,并且您永远不会处理最终值。

我还建议在输出语句的末尾添加一些换行符。

这是一个固定的程序:

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <string>
#include <algorithm>

int main(){

    int n;
    std::cin >> n;

    // Get all the numbers 
    std::string rawInput;
    std::cin.ignore(256,'\n'); 
    std::getline(std::cin, rawInput);

    std::vector<std::string> numbers;
    std::string number = ""; 
    for (size_t i = 0; i < rawInput.size(); i++) {
        char c = rawInput[i];
        if (c == ' ') {
            numbers.push_back(number);
            number = "";
        }
        number += c; 
    }

    // One more! If there wasn't a space at the end of it.
    if (!number.empty())
        numbers.push_back(number);

    // Get all the ints
    int sum = 0;
    for (size_t j = 0; j < numbers.size(); j++) { 
        sum += stoi(numbers[j]); 
    } 

    std::cout << sum << '\n'; 
}

live demo

我实际上建议只是坚持使用格式化提取,这更简单:

#include <vector>
#include <iostream>
#include <numeric>
#include <algorithm>
int main()
{
    int n;
    std::cin >> n;

    // Get all the numbers 
    std::vector<int> numbers;
    int temp;
    while (std::cin >> temp)
        numbers.push_back(temp);

    const int sum = std::accumulate(numbers.begin(), numbers.end(), 0);
    std::cout << sum << '\n'; 
}

请注意,我们根本不需要n!但是,如果您想人为地将提取限制为n个数字,则可以在while循环中执行此操作。

live demo

答案 1 :(得分:2)

此行读取单个以空格分隔的标记(请参阅documentation):

std::cin >> rawInput;

并且此循环对该标记中的每个字符运行一次,对于该行的每个整数(rawInput将仅包含第一个整数)

for (int i = 0; i < rawInput.size(); i++) {

最后这个循环不会检查nnumbers.size()是否完全相关:

for (int j = 0; j < n; j++) {

所以你:

  • 使用错误的循环条件超出numbers的范围
  • 没有理解或检查rawInput
  • 的内容
  • 没有理解或检查格式化输入操作符的工作方式。

只是打印您的变量值(或在gdb中检查它们)会显示这一点 - 打印we have reached here 1 很多没有打印the value of rawInput is "6"那么有用(这会给你一个非常强烈的提示)。

除此之外,正确的代码实际上可以使用那些格式化的输入运算符来完成大部分工作:

#include <iostream>

int main()
{
    unsigned N = 0;
    std::cin >> N;
    if (!std::cin) return -1;

    unsigned SUM = 0;
    for (unsigned i = 0; i < N; ++i) {
        unsigned val;
        std::cin >> val;
        if (!std::cin) return -2;
        SUM += val;
    }
    std::cout << SUM << '\n';
}

我甚至包括了样本错误检查,你可能会从hackerrank测试中省略它。它仍然比您的版本更短,更简单。

答案 2 :(得分:1)

您使用的方法不正确!您假设该数字只有一位数,而这可能并非总是如此。

您只需在cin循环中使用for并将所有数字存储在vector中。

#include <iostream>
#include <vector>

int main() {
    int n;
    std::vector<int> numbers;
    std::cin >> n;
    for(int i=0;i<n;++i)
    {
        int no;
        std::cin >> no;
        numbers.push_back(no);
    }
    long sum = 0;
    for (auto i : numbers)
        sum += i;
    std::cout << sum;
    return 0;
}

<强>输入

5
1 2 31 4 5

<强>输出

43

答案 3 :(得分:0)

哦,我一直都看到这个。这很微妙。

std::cin >> n;

仅读取第一个int。它没有注意到之后的行尾。因此,当您准备好读取该字符串时,它将转到该行的末尾 - 第一行,即其上有n的行。

修正:

std::cin >> n;
std::cin.ignore(256,'\n'); //ignore up thru next '\n' or 256 chars, whichever is first

这会跳过该行尾,让您从下一行开始。

另一个修复,不太健壮:

std::cin >> n;
std::cin.get(); //Gets the next char, which in this case happens to be '\n'

您还需要将std::cin >> rawInput;更改为

char rawInputCharStar [SOMEBIGNUM];
std::cin.getline (rawInputCharStar,SOMEBIGNUM);
rawInput = rawInputCharStar

所以它可以读入多个字符串。 (我不得不去char *因为getline需要一个char *。可能会有更优雅的方式。)

当你进入for循环时,你会发现字符串中的数字不是字符串的 size ,所以你需要不同的边界。

(当然,我不知道为什么你不仅仅是以整体形式阅读它们,但你必须有理由。)