【问题标题】:Why am I getting a timeout(using sstream to parse list of integers separated by commas)?为什么我会超时(使用 sstream 解析以逗号分隔的整数列表)?
【发布时间】:2015-11-25 23:06:36
【问题描述】:

我得到一个由 n 个整数组成的输入字符串,用逗号分隔(例如“23,4,56”)。我需要设置一个字符串流来表示这个字符串,然后用它将每个整数扫描成一个向量。向量的元素(列表中的整数)最终将逐行输出。我得到了 main(),并且只负责编写 parseInts(string str)。出于某种原因,我一直在超时。我猜这是我的while循环中的一些东西,特别是关于我如何用str()操作我的sstream,但我无法弄清楚到底发生了什么。我是 sstream 和 C++ 的新手,所以任何帮助都将不胜感激!

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

vector<int> parseInts(string str) {
    int a; //will use this to hold the value of the 1st int in the sstream
    stringstream list_initial; //will iterate over this sstream
    list_initial.str(str); //set sstream to represent input str
    vector<int> list_final; //will return this final vector for output in main
    while (!list_initial.str().empty()){ //stop iterating at end of string
        list_initial>>a; //store leading int value in a
        list_final.push_back(a); //add a to end of vector
        while (!ispunct(list_initial.str()[0])){ //get to next int in list
            list_initial.str(list_initial.str().erase(0,1));
        };
        list_initial.str(list_initial.str().erase(0,1)); //erase leading comma
    };
    return list_final;   
};

int main() {
    string str;
    cin >> str;
    vector<int> integers = parseInts(str);
    for(int i = 0; i < integers.size(); i++) {
        cout << integers[i] << "\n";
    }

    return 0;
}

【问题讨论】:

  • “超时”是什么意思?如果您的代码运行缓慢,我怀疑这是因为您正在创建所有 std::strings。 std::stringstream 和其他流一样是流,您应该这样对待它。您无需继续创建 str()ing 或重置流的 str()ing。
  • 这是什么意思?我真的不明白流是如何工作的。我认为理想情况下我可以编写一些只使用在列表中读取的流成员函数,忽略逗号并在列表末尾停止的东西。我该怎么做?
  • 您不断地通过创建和销毁新字符串来修改流。你不需要这样做。请参阅下面的代码进行演示。

标签: c++ timeout stringstream sstream


【解决方案1】:

您的功能实现可以真正得到改进,但是按照您的逻辑,如果您替换第二条while 行:

        while (!ispunct(list_initial.str()[0])){ //get to next int in list

通过这个,添加一个长度检查:

        while (list_initial.str().size() && !ispunct(list_initial.str()[0])){ //get to next int in list

,然后运行,运行良好,退出良好。

说明

这个循环从未中断,因为在进程结束时,ispunct() 函数从未将其参数!list_initial.str()[0] 识别为真实的:此时字符串list_initial.str() 为空,然后@987654328 @index 不存在。
请查看cplusplus.com上的文档:

如果 pos 等于字符串长度,则 const 版本从不抛出异常(保证不抛出)。
否则,它会导致未定义的行为。

您的程序被冻结,因为它没有找到可能导致退出上述循环的正确条件。

建议

关于您的问题要讨论的重要一点是:为什么您自己没有找到答案?这个问题的一个答案如下:您没有尝试调试您的代码。

为了回答您的问题,我刚刚调试了您的代码,并通过在您的代码周围添加这种行来快速找到问题,看看发生了什么:

cout << "list_initial: " << list_initial.str() << endl;

您的代码示例,添加了调试语句::

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

vector<int> parseInts(string str) {
    cout << "begin parseInts()" << endl;  // ######  DEBUG  ######

    int a; //will use this to hold the value of the 1st int in the sstream
    stringstream list_initial; //will iterate over this sstream
    list_initial.str(str); //set sstream to represent input str
    vector<int> list_final; //will return this final vector for output in main

    cout << "begin while 1" << endl;  // ######  DEBUG  ######
    while (!list_initial.str().empty()){ //stop iterating at end of string
        list_initial >> a; //store leading int value in a
        list_final.push_back(a); //add a to end of vector

        cout << "a: " << a << endl;  // ######  DEBUG  ######
        cout << "begin while 2" << endl;  // ######  DEBUG  ######
        while (!ispunct(list_initial.str()[0])){ //get to next int in list

            cout << "list_initial: " << list_initial.str() << endl;  // ######  DEBUG  ######

            list_initial.str(list_initial.str().erase(0,1));
        };
        cout << "endwhile 2" << endl;  // ######  DEBUG  ######

        list_initial.str(list_initial.str().erase(0,1)); //erase leading comma
    };
    cout << "endwhile 1" << endl;  // ######  DEBUG  ######

    cout << "end parseInts()" << endl;  // ######  DEBUG  ######

    return list_final;   
};

int main() {
    string str;
    cin >> str;
    vector<int> integers = parseInts(str);

    cout << "begin for" << endl;  // ######  DEBUG  ######
    for(int i = 0; i < integers.size(); i++) {
        cout << integers[i] << "\n";
    }
    cout << "end for" << endl;  // ######  DEBUG  ######

    return 0;
}

这种(非常基本的)调试技术可以让您快速找出代码中的错误;只需在代码中添加一些cout &lt;&lt; ... &lt;&lt; endl; 行以指出一些重要信息,即:“哪个循环导致冻结?”或“此时此变量的内容是什么?”。

如果您将cout &lt;&lt; "Entering While loop 1" &lt;&lt; endl; 放在每个可疑循环之前,将cout &lt;&lt; "Exiting While loop 1" &lt;&lt; endl; 放在每个可疑循环之后,您可能会了解很多关于程序执行期间发生的事情。

加上这个调试行,你可以很容易地发现第二个循环一直在循环;那么你可以将你的错误调查缩小到这个循环。

【讨论】:

  • 我明白了,非常感谢。我是编码新手,所以调试技巧非常有帮助。你说我的功能实现可以改进,我很确定。我认为我并不真正了解 sstreams 或流在一般情况下是如何工作的。鉴于对 sstreams 的理解,什么是优雅的实现?
【解决方案2】:

流是一个抽象文件。它只是一堆可以理解的文本,例如,cin &gt;&gt; n 将数字转换为整数。

这里有一个关于如何使用流的想法:

#include <cctype>
#include <ciso646>
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;

vector<int> parse_ints( const string& s )
{
  vector<int> ints;     // resulting list of ints
  istringstream ss(s);  // stream of ints separated by (non-digits)
  int n;                // each int extracted from the stream

  // while input an int
  while (ss >> n)
  {
    // save the int
    ints.push_back(n);

    // skip to next digit
    while (ss and !isdigit(ss.peek())) ss.get();
  }

  return ints;
}

int main()
{
  vector<int> xs = parse_ints( "2, 3 ,5; 7 : 11.13\t17abc21" );
  for (int x : xs)
    cout << x << " ";
  cout << "\n";
}

这会产生:

2 3 5 7 11 13 17 21

注意我们如何可以简单地跳过我们不感兴趣的字符?在这种特殊情况下,我们跳过所有个非数字字符。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 2015-02-01
    • 2013-02-14
    • 2020-12-06
    相关资源
    最近更新 更多