替换atoi() 很容易:
int strToInt(const char *text)
{
int n = 0, sign = 1;
switch (*text) {
case '-': sign = -1;
case '+': ++text;
}
for (; isdigit(*text); ++text) n *= 10, n += *text - '0';
return n * sign;
}
(ideone 上的演示)
替换已经可用的东西似乎没有多大意义。因此,我想提一些关于这个的想法。
可以根据具体的个人需求调整实施:
- 可以添加整数溢出检查
- 可能会返回
text 的最终值(如strtol())以检查已处理的字符数或对其他内容进行进一步解析
- 变体可能用于
unsigned(不接受符号)。
- 可能接受也可能不接受前面的空格
- 可以考虑特殊语法
- 还有其他超出我想象的东西。
将此想法扩展到其他数字类型,例如float 或 double,变得更有趣了。
由于浮点数肯定是本地化的主题,因此必须考虑这一点。 (关于十进制整数,我不确定可以本地化什么,但即使是这种情况。)如果实现了具有浮点数语法的文本文件阅读器(如在 C 中),您可能不会忘记将语言环境调整为 @ 987654335@ 之前使用strtod()(使用setlocale())。 (作为德国人,我对这个话题很敏感,因为在德语语言环境中,'.' 和 ',' 的意思就像英语中的反之亦然。)
{ const char *localeOld = setlocale(LC_ALL, "C");
value = strtod(text);
setlocale(LC_ALL, localeOld);
}
另一个事实是,考虑语言环境(即使调整为 C)似乎有点昂贵。几年前,我们实现了自己的浮点阅读器来替代strtod(),它在COLLADA 阅读器中提供了60 ... 100 的加速(文件通常提供大量浮点数的XML 文件格式) .
更新:
受到 Paul Floyd 反馈的鼓舞,我很好奇如何更快地strToInt()。因此,我构建了一个简单的测试套件并进行了一些测量:
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int strToInt(const char *text)
{
int n = 0, sign = 1;
switch (*text) {
case '-': sign = -1;
case '+': ++text;
}
for (; isdigit(*text); ++text) n *= 10, n += *text - '0';
return n * sign;
}
int main(int argc, char **argv)
{
int n = 10000000; /* default number of measurements */
/* read command line options */
if (argc > 1) n = atoi(argv[1]);
if (n <= 0) return 1; /* ERROR */
/* build samples */
assert(sizeof(int) <= 8); /* May be, I want to do it again 20 years ago. */
/* 24 characters should be capable to hold any decimal for int
* (upto 64 bit)
*/
char (*samples)[24] = malloc(n * 24 * sizeof(char));
if (!samples) {
printf("ERROR: Cannot allocate samples!\n"
"(Out of memory.)\n");
return 1;
}
for (int i = 0; i < n; ++i) sprintf(samples[i], "%d", i - (i & 1) * n);
/* assert correct results, ensure fair caching, pre-heat CPU */
int *retAToI = malloc(n * sizeof(int));
if (!retAToI) {
printf("ERROR: Cannot allocate result array for atoi()!\n"
"(Out of memory.)\n");
return 1;
}
int *retStrToInt = malloc(n * sizeof(int));
if (!retStrToInt) {
printf("ERROR: Cannot allocate result array for strToInt()!\n"
"(Out of memory.)\n");
return 1;
}
int nErrors = 0;
for (int i = 0; i < n; ++i) {
retAToI[i] = atoi(samples[i]); retStrToInt[i] = strToInt(samples[i]);
if (retAToI[i] != retStrToInt[i]) {
printf("ERROR: atoi(\"%s\"): %d, strToInt(\"%s\"): %d!\n",
samples[i], retAToI[i], samples[i], retStrToInt[i]);
++nErrors;
}
}
if (nErrors) {
printf("%d ERRORs found!", nErrors);
return 2;
}
/* do measurements */
enum { nTries = 10 };
time_t tTbl[nTries][2];
for (int i = 0; i < nTries; ++i) {
printf("Measurement %d:\n", i + 1);
{ time_t t0 = clock();
for (int i = 0; i < n; ++i) retAToI[i] = atoi(samples[i]);
tTbl[i][0] = clock() - t0;
}
{ time_t t0 = clock();
for (int i = 0; i < n; ++i) retStrToInt[i] = strToInt(samples[i]);
tTbl[i][1] = clock() - t0;
}
/* assert correct results (and prevent that measurement is optimized away) */
for (int i = 0; i < n; ++i) if (retAToI[i] != retStrToInt[i]) return 3;
}
/* report */
printf("Report:\n");
printf("%20s|%20s\n", "atoi() ", "strToInt() ");
printf("--------------------+--------------------\n");
double tAvg[2] = { 0.0, 0.0 }; const char *sep = "|\n";
for (int i = 0; i < nTries; ++i) {
for (int j = 0; j < 2; ++j) {
double t = (double)tTbl[i][j] / CLOCKS_PER_SEC;
printf("%19.3f %c", t, sep[j]);
tAvg[j] += t;
}
}
printf("--------------------+--------------------\n");
for (int j = 0; j < 2; ++j) printf("%19.3f %c", tAvg[j] / nTries, sep[j]);
/* done */
return 0;
}
我在一些平台上试过这个。
Windows 10(64 位)上的 VS2013,发布模式:
Report:
atoi() | strToInt()
--------------------+--------------------
0.232 | 0.200
0.310 | 0.240
0.253 | 0.199
0.231 | 0.201
0.232 | 0.253
0.247 | 0.201
0.238 | 0.201
0.247 | 0.223
0.248 | 0.200
0.249 | 0.200
--------------------+--------------------
0.249 | 0.212
cygwin 上的 gcc 5.4.0,Windows 10(64 位),gcc -std=c11 -O2:
Report:
atoi() | strToInt()
--------------------+--------------------
0.360 | 0.312
0.391 | 0.250
0.360 | 0.328
0.391 | 0.312
0.375 | 0.281
0.359 | 0.282
0.375 | 0.297
0.391 | 0.250
0.359 | 0.297
0.406 | 0.281
--------------------+--------------------
0.377 | 0.289
在codingground上上传并执行的示例
Linux 3.10.0-327.36.3.el7.x86_64 上的 gcc 4.8.5,gcc -std=c11 -O2:
Report:
atoi() | strToInt()
--------------------+--------------------
1.080 | 0.750
1.000 | 0.780
0.980 | 0.770
1.010 | 0.770
1.000 | 0.770
1.010 | 0.780
1.010 | 0.780
1.010 | 0.770
1.020 | 0.780
1.020 | 0.780
--------------------+--------------------
1.014 | 0.773
嗯,strToInt() 快了一点。 (没有-O2,它甚至比atoi()慢,但标准库可能也被优化了。)
注意:
由于时间测量涉及赋值和循环操作,这提供了关于哪个更快的定性陈述。它没有提供定量因素。 (要得到一个,测量会变得更加复杂。)
由于atoi() 的简单性,应用程序不得不非常经常使用它,直到它变得值得考虑开发工作...