我有一个大文件,其中每行包含以空格分隔的整数。任务是逐行稀疏此文件。对于字符串到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)上测试,我得到以下时间:
我的问题:你知道如何通过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
版本一样有效,它会产生效果。
答案 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;
}