【问题标题】:C++ IO optimization and string handling for stdin标准输入的 C++ IO 优化和字符串处理
【发布时间】:2013-01-24 00:01:27
【问题描述】:

我正在为使用 C++ 的“标准标准输入程序”编写一个竞赛程序。输入的第一行表示从那时起预计有多少行 (x) 作为输入。这些输入行可以是字符串、整数或两者的某种组合,并且每一行恰好包含两个由空格分隔的元素。目前,我以类似于以下的方式一次接收每一行(在请求下一行之前处理信息):

  string initial;
  getline (cin,initial);
  istringstream stringStream (initial);
  vector<string> parsedString;
  vector<int> data;
  char splitToken = ' ';
  while ( !stringStream.eof() )
  {
    string subString;
    getline( stringStream, subString, splitToken);
    parsedString.push_back( subString );
  }

  for (int i = 0; i <parsedString.size(); i++)
  {
    string temp = parsedString[i];
    int intTemp = atoi(temp.c_str());
    data.push_back(intTemp);
  }
  unsigned int n = data[0];
  unsigned int m = data[1];

在这种特定情况下,我知道传入的数据将是两个整数,但情况并非总是如此。我想知道是否有某种方法可以使我的代码更快,或者通过改变我的方法(也许在知道有多少输入行之后一次获取所有输入行)或者通过使用更好的内置 C++ 函数来拆分传入的行空间到组成它们的两个元素。

谢谢

【问题讨论】:

  • 哇哦,这是很多开销/矫枉过正
  • Facebook 黑客杯又来了 :-) you 不应该解决这个问题吗? (我想我不会在周末指出任何与eof() 相关的问题!)
  • 您可能会发现此链接很有用:stackoverflow.com/questions/1042110/…
  • 你怎么知道哪些是字符串,哪些是数字?
  • data.push_back(atoi(parsedString[i].c_str())); 是一个多合一的特别版。

标签: c++ performance methods io


【解决方案1】:

通常,在 C++ 中读取输入的习惯用法更符合以下原则:

std::ios_base::sync_with_stdio(false); //tell iostreams to be fast
int number_of_lines;
std::cin >> number_of_lines;
if (!std::cin) *ERROR*;

for( ; number_of_lines>0; --number_of_lines) {
    unsigned n, m;
    std::cin >> n >> m;
    if (!std::cin) *ERROR*;
    //process n and m here
}

您说“这些输入行可以是字符串、整数或两者的某种组合”,但您如何知道哪些是哪些?上面的代码假设一切都是一个数字,因为如果你不知道类型,输入是没有用的。

【讨论】:

  • 我会根据具体情况知道,这对我的总体问题并不重要。感谢你的回答!我的一个问题是,有什么比 cin 更快的吗?
  • @user1998665:是的,如果绝对需要速度,scanf 可以以几乎类似的方式使用,有时它会稍微快一点:ideone.com/e8PWn2
  • @ildjarn:根据您对另一个答案的评论,我在 35 分钟前将其放入我的答案中。我做错了吗? (不足为奇)
  • @user1998665: 小心 scanf,一个错字,它会在没有任何警告的情况下摧毁一切。 cin 是完全安全的,如果你犯了错误会给出警告。
  • @MooingDuck :不,我只是没有阅读您的代码,我的错。 :-]
【解决方案2】:

好的,如果您想谈谈其他方法,这里有另一种方法: 几乎所有与字符串一起工作的代码的大(性能)问题,即有大量复制操作......一种天真的方法(初学者使用)是制作大量临时字符串当从字符串或内存缓冲区中查找/解析/提取...

另一种方法(当源缓冲区的生命周期允许时)是从缓冲区中查找、解析或提取数据,而无需复制源字符串的任何特定部分。为此,可以使用boost::range 库和boost::iterator_range 类(与boost string_algo 结合使用)。这样可以执行通常的查找/解析任务并避免复制部分字符串。

我的一个例子: 前段时间(用于性能测试)我的同事写了 3 个版本的相同配置解析器(配置文件大约几兆字节):

  • 使用std::string::find
  • 使用boost::tokenizer
  • boost::iterator_range

结果令人惊讶:

  • 使用 -O0 优化 std::string::find 获胜(大约是 boost::tokenizer 的 3 倍,大约是基于 iterator_range 的解析器的 5 倍)
  • 但在 -O3 时一切都变了:boost::iterator_range 是绝对的赢家! (大约是std::string::find 的 12 倍!)

结果非常可预测:boost::string_algo 和朋友的高度模板化代码也高度内联...

这就是为什么我个人喜欢使用boost::iterator_range 来解析字符串...


所以我建议将所有内容(尽可能多地)读入一个std::stringstd::vector&lt;char&gt;(更好的一个,因为它具有内存连续性保证,所以您可以将read() 从流中直接读入容器) 或std::deque&lt;char&gt;,然后使用boost::string_algoboost::iterator_range 对其进行解析,以避免任何(无用的)从源字符串复制到临时位置... 也尝试使用boost::lexical_cast 转换数字(或您自己的范围感知数字转换器)

还有一个建议:尽量避免内存(通常)分配——这确实是繁重的操作。 当您知道您想要存储的数据的可能大小时,请使用rserve() 成员作为容器。最后,尝试使用自定义(棘手的)分配器而不是(通常是愚蠢的)默认分配器。

注意在 C++11 中 std::string 具有内存连续性保证 std::vector

【讨论】:

  • 好先生,就编程能力而言,您比我领先很多。我为你的奉献精神鼓掌。 起立鼓掌
【解决方案3】:

在许多系统上(包括 Linux 上的 GCC),C++ I/O 流非常慢。如果速度是您最关心的问题,请使用&lt;stdio.h&gt;,例如scanf() 和(甚至更快:)getchar(),并手动进行解析,就像在下面的状态机中一样:

int c;
int state = 0;
while ((c = getchar()) >= 0) {
  switch (c) {
   case ' ': case '\t':
    ...;
    ... if (state == ...) { ...; state = ...; }
    break;
   case '\n':
    ...;
    break;
   default:
    ...;
  }
}

尽可能少地复制数据。 (例如,在问题string temp = ... 做了一个不必要的复制,可以通过使用引用消除它:string &amp;temp = ...。)如果可能,使用状态找出下一个字符c 的最终位置,并且把它放在那里,以后不要复制或移动它。

【讨论】:

  • 我敢打赌,如果您首先致电 std::ios_base::sync_with_stdio(false),您会发现标准 IO 流执行得很好(对于大多数用途)。
猜你喜欢
  • 1970-01-01
  • 2013-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-01
  • 2013-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多