将字符串转换为int

时间:2010-01-07 20:46:15

标签: c++ string performance

我有一个大文件,其中每行包含以空格分隔的整数。任务是逐行稀疏此文件。对于字符串到int的转换,我有三个解决方案:

static int stringToIntV1(const string& str) {
    return (atoi(str.c_str()));
}

但是,如果我传递格式错误的字符串,则不会产生任何错误。例如,字符串“123error”将转换为123。

第二个解决方案:

static int stringToIntV2(const string& str)
{
    int result;
    istringstream myStream(str);

    if (myStream >> result) {
        return result;
    }
    // else
    throw domain_error(str + " is not an int!");
}

我在这里遇到同样的问题,格式错误的字符串不会引发错误。

Boost的第三个解决方案(在Boost Library找到):

static int stringToIntV3(const string& str)
{
    int iResult = 0;
    try {
        iResult = lexical_cast<int>(str);
    }
    catch(bad_lexical_cast &) {
        throw domain_error(str + " is not an int!");
    }
    return iResult;
}

这个给出了正确的结果。

但是,执行时间存在显着差异。在大文本文件(32 MB)上测试,我得到以下时间:

  • (1)与atoi:4.522s(获胜者)
  • (2) with istringstream:15.303s(非常慢)
  • (3)与lexical_cast:10.958s(两者之间)

我的问题:你知道如何通过atoi注意格式错误的字符串吗?它将提供最快的解决方案。或者你知道更好的解决方案吗?

更新:感谢您的回答。按照提示,我想出了这个解决方案:

static int stringToIntV4(const string& str)
{
    char * pEnd;
    const char * c_str = str.c_str();
    int result = strtol(c_str, &pEnd, 10);
    if (pEnd == c_str+str.length()) {
        return result;
    }
    // else
    throw domain_error("'" + str + "'" + " is not an int!");
}

好消息是,如果问题atoi版本一样有效,它会产生效果。

7 个答案:

答案 0 :(得分:13)

我使用strtol。它设置的参数指向它无法转换的第一个字符,因此您可以使用它来确定是否转换了整个字符串。

编辑:就速度而言,我希望它比atoi略慢,但比你试过的其他人要快。

答案 1 :(得分:2)

strtol函数返回指向字符串中下一个字符的指针。您可以在转换后检查该字符,看它是否不是空格,表示错误。我进行了一个简单的测试,strtol的性能似乎与atoi相当。

答案 2 :(得分:2)

我知道这个已被现有功能所覆盖。如果性能是最重要的考虑因素,那么编写自己的转换以完成您所需的操作并不是更多的工作。例如,如果您知道没有任何前导空格或所有数字都是正数,请不要处理这些情况。 编译器也应该能够内联这个函数。

#include <ctype.h>
#include <string>

inline int customStringToInt( const std::string &str ) {
  register const char *p = str.c_str(), *pEnd = p + str.size();

  // Remove leading whitespace (remove if no leading whitespace).
  while( p != pEnd && isspace(*p) ) ++p;

  // Handle neg/pos sign (remove if no sign).
  int sign = 1;
  if( p != pEnd ) {
    if(      *p == '-' ) { ++p; sign = -1; }
    else if( *p == '+' ) { ++p; }
  } 

  // String without any digits is not OK (blank != 0) (remove if it is OK).
  if( p == pEnd ) throw domain_error("'" + str + "'" + " has no digits."); 

  // Handle digits.
  register int i = 0;
  while( p != pEnd )
    if( isdigit(*p) ) i = i * 10 + (*p++ - '0');
    else throw domain_error("'" + str + "'" + " contains non-digits.");

  return sign * i;
}

答案 3 :(得分:1)

您可以在调用atoi之前检查字符串吗?

isdigit()是一个非常棒的小函数,编写isalldigits()以便在紧密,快速的循环中检查字符串应该没有问题。

(如果您碰巧使用小数或+/-符号,您可能也想添加它)

因此,只需验证字符串是否为全数字,然后调用atoi。 应该很快。

答案 4 :(得分:1)

我做了自己的小测试。 与使用流操作符一样,使用atoi()似乎需要大约相同的时间。 我有一个包含2,000,000个号码的文件。每行10个数字(虽然代码不使用这个事实)。

第一个版本使用atoi(),我承认我花了一段时间才弄清楚并且可能更有效。接受更新并使其更有效。

流一。用了20秒的时间来编写和开箱即用 时间结果是:

> time ./a.exe 1
AtoI()
6.084u 0.156s 0:06.33 98.4%     0+0k 0+0io 8619pf+0w
> time ./a.exe
Iterator()
4.680u 0.296s 0:05.05 98.4%     0+0k 0+0io 6304pf+0w

代码:

#include <vector>
#include <iostream>
#include <iterator>
#include <fstream>
#include <iostream>

int main(int argc,char* argv[])
{
    std::vector<int>    data;
    std::ifstream       vals("rand.txt");

    if (argc > 1)
    {
        std::cout << "AtoI()\n";

        std::string line;
        while(std::getline(vals,line))
        {
            std::string::size_type loop = 0;
            while(loop < line.size())
            {
                while(isspace(line[loop]) && (loop < line.size()))
                {   ++loop;
                }
                std::string::size_type end = line.find_first_not_of("0123456789",loop);
                data.push_back(atoi(line.substr(loop,end - loop).c_str()));

                loop = end;
            }

        }
    }
    else
    {
        std::cout << "Iterator()\n";
        std::copy(  std::istream_iterator<int>(vals),
                    std::istream_iterator<int>(),
                    std::back_inserter(data)
                 );
    }
}

答案 5 :(得分:0)

数字中的任何前导零?如果atoi返回零并且数字的第一个数字字符不是'0',那么我们就有错误。

答案 6 :(得分:0)

为什么不在有有效文件指针的情况下转换它,而不是将其加载到字符串然后解析整数?基本上你在浪费记忆和时间。

在我看来,你应该这样做:

// somewhere in your code
ifstream fin("filename");
if(!fin) {
 // handle error
}

int number;
while(getNextNumber(fin, number))
{
 cout << number << endl;
}
fin.close();
// done

// some function somewhere
bool getNextNumber(ifstream& fin, int& number)
{
 while(!(fin >> number))
 {
  fin.clear();
  fin.ignore(numeric_limits<streamsize>::max(),'\n');
 }

 if(!fin || fin.eof())
  return false;
 return true;
}