C++ 标准输入的 IO 优化和字符串处理



我正在为一个使用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++函数将空间处的传入行拆分为组成它们的两个元素。

谢谢

通常,在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
}

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

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

另一种方法(当源缓冲区的生存期允许时)是从缓冲区中查找、解析或提取数据,而无需复制源字符串的任何特定部分。 为此,可以使用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是绝对的赢家!(大约 12 倍超过 std::string::find

结果是可以预见的:高度模板化的boost::string_algo和朋友代码也非常活跃......

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


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

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

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

在许多系统上(包括Linux上的GCC),C++I/O流非常慢。如果速度是您最关心的问题,请使用<stdio.h>,例如 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 &temp = ... 。如果可能的话,使用状态找出下一个字符c的最终位置,并把它放在那里,以后不要复制或移动它。

最新更新