我正在尝试从正在解析的命令行中找出哪个函数最好将十进制,十六进制或八进制数转换为最好的int,而无需事先知道输入。
然后的目标是使用一个可以识别不同类型输入的函数,并将其分配给其整数(int)值,然后可以使用该值:
可以打印
1 2 3
| 23
196 /*hexadecimal*/
56 /*octal*/ |
我可以看到的唯一问题是解析,以查找十进制整数和八进制之间的差异。
附带的问题是,将字符串转换为整数以便使用是否稳定?
-
atoi()和atol()在错误恢复方面非常受限制; sscanf()太复杂了;使用strtol()或strtoul()。
-
整数常量的前导0(零)表示八进制;前导0x或0X表示十六进制,如果可以的话,应坚持使用此类输入。
-
"不事先知道输入"是不是您不知道数字的相应基数?没有通用的方法可以从数字的数字推断其底数。例如," 70"可以是8或10或16。
-
没有关于表示的含义的信息,没有人可以解释它。通常,我们使用文化民俗来传达此信息(例如,"以0开头的八进制");如果您想要与众不同的东西,则您a)会使每个人都不满意,b)必须编写您自己的代码。
-
抱歉,我不确定八进制和十六进制的前导0或0x,我无意与之分开。
-
如果将0作为base参数传递给strtol或strtoul,它们将使用tesseract提到的前缀来检测输入所在的碱基,但仅在其可能的范围内。因此,如果希望将70视为八进制,则需要使用070,然后strtoul("070", NULL, 0)将返回56(十进制)。我非常确定strtoul("C4", NULL, 0)会返回196,尽管没有0x前缀,但这只是因为C放弃了它是十六进制的事实。
-
@MikeHolt您应该将其发布为答案。恐怕"C4"不会转换为十六进制,但是前缀是必需的。这些规则与source中的整数文字相同,这是有意义的。
-
@MikeHolt:不,它需要" 0xC4"或" 0XC4"
-
你是对的。我刚刚检查。
-
另请参阅:正确使用strtol()等。
-
清楚一点,您目标的一部分是知道原始文档的基数(8,10,16),并使用" / * octal * /","或" / * hexadecimal * /"报告该基数吗?文本到int转换?
-
将字符串转换为整数C的可能重复项
which function would be best to convert either a decimal, hexadecimal, or octal number to an int the best (?)
要将此类文本转换为int,如果需要,建议在转换为int时建议long strtol(const char *nptr, char **endptr, int base);进行其他测试。
使用0作为base,以10、16或8为基数评估转向转换中的早期字符。@Mike Holt
1 2 3
| 0x or 0X followed by hex digits--> hexadecimal
0 --> octal
else --> decimal |
样例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <errno.h>
#include <limits.h>
#include <stdlib.h>
int mystrtoi (const char *str ) {
char *endptr ;
errno = 0;
// v--- determine conversion base
long long_var = strtol(str , &endptr , 0);
// out of range , extra junk at end, no conversion at all
if (errno == ERANGE || *endptr != '\0' || str == endptr ) {
Handle_Error ();
}
// Needed when `int` and `long` have different ranges
#if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
if (long_var < INT_MIN || long_var > INT_MAX ) {
errno = ERANGE ;
Handle_Error ();
}
#endif
return (int) long_var ;
} |
atoi vs atol vs strtol vs strtoul vs sscanf ... to int
atoi()
优点:非常简单。
专业版:转换为int。
Pro:在C标准库中。
优点:快。
缺点:没有错误处理。
缺点:既不处理十六进制也不处理八进制。
atol()
优点:简单。
Pro:在C标准库中。
优点:快。
缺点:转换为大小可能不同的long,而不是int。
缺点:没有错误处理。
缺点:既不处理十六进制也不处理八进制。
strtol()
优点:简单。
Pro:在C标准库中。
优点:良好的错误处理能力。
优点:快。
Pro:可以处理二进制文件。
缺点:转换为大小可能不同的long,而不是int。
strtoul()
优点:简单。
Pro:在C标准库中。
优点:良好的错误处理能力。
优点:快。
Pro:可以处理二进制文件。
---:似乎不抱怨负数。
缺点:转换为unsigned long,而不是int,大小可能有所不同。
sscanf(...,"%i", ...)
Pro:在C标准库中。
专业版:转换为int。
---:复杂的路途。
缺点:可能会变慢。
缺点:可以进行错误处理(未定义溢出)。
所有人都从locale设置中受益/受益。 §7.22.1.46"在" C"语言环境之外,还可以接受其他特定于语言环境的主题序列形式。"
其他学分:
@Jonathan Leffler:errno针对ERANGE,atoi()仅十进制的测试,关于errno多线程问题的讨论。
@玛丽安速度问题。
@凯文图书馆的包容性。
要转换short,signed char等,请考虑strto_subrange()。
-
strtoul的stroul错字。您是什么意思不重入(或更准确地说,为什么不重入)?
-
@乔纳森·莱夫勒(Jonathan Leffler)"不可重入"令人困惑。我担心的是,需要澄清errno,调用strtox()和测试errno,但是errno可能会由于其他并发进程而发生变化。
-
反对sscanf的要点是,与其他人相比,它非常慢。
-
@Marian关于速度:一些编译器将分析scanf()格式并调整scanf()代码。在嵌入式设计中可以看到这一点,尤其是确定是否需要浮点包时。如果已知所有scanf()格式都称为有限格式格式,则会进行优化,从而极大地提高速度并减少代码占用量。
-
您说errno可能由于其他并发进程而改变。假设s / processes / threads /符合您的意思,那么在线程环境中,errno是特定于线程的,因此不会(不应)出现问题,这仍然是事实。 ISO / IEC 9899:2011第7.5节错误表示:errno扩展为具有类型int和线程本地存储持续时间的可修改左值,该值由多个库函数设置为正错误号。脚注201说明errno不必是对象的标识符(例如,它可以是*errno())。
-
@Jonathan Leffler很高兴知道errno是线程特定的-我对此有模糊的信息。顺便说一句:locale可能会影响所有这些方法。"在" C"语言环境之外,还可以接受其他特定于语言环境的主题序列形式。"您是否认为locale是线程安全的?
-
有关语言环境的问题肯定会使事情复杂化;我将不得不对此进行研究,这可能取决于实施情况。如果您在多个语言环境和多个线程之间乱搞,那么您肯定有很多事要做。 (ISO / IEC 9899:2011第7.11.1.1节,setlocale函数表示:调用setlocale函数可能会导致数据竞争以及对setlocale函数的其他调用或对受setlocale函数影响的函数的调用当前语言环境。但这可能只是问题的开始。)
-
我对atoi与strtol的强烈歧义感到困惑。我在stdlib.h中进行了查看,atoi被简单地实现为return (int) strtol (__nptr, (char **) NULL, 10);(gcc 5.4.0)。真的有什么区别?
-
@ArturCzajka 1)使用(int) strtol (__nptr, (char **) NULL, 10);时,如果转换使long超出int的范围,则会丢失信息。2)(char **) NULL会丢失有关转换停止位置的信息。 3)错误时,即使在您今天使用的编译器上,实现也不必遵循(int) strtol (__nptr, (char **) NULL, 10);。错误时,行为未定义。 strtol()没有这些缺点。
如果您关心错误情况,仅考虑使用strtol()和strtoul()(或中的strtoll()或strtoull()或中的strtoimax()或strtoumax())是明智的。如果您不关心溢出时的错误情况,则可以使用其中任何一个。 atoi()或atol()或sscanf()都不能控制值是否溢出。此外,atoi()和atol()都不支持十六进制或八进制输入(因此,实际上您不能使用它们来满足您的要求)。
请注意,调用strtoX()函数并非完全无关紧要。调用它们之前,必须将errno设置为0,并传递一个指针以获取结束位置,并仔细分析以了解发生了什么。请记住,这些函数的所有可能返回值都是有效输出,但是其中一些可能还指示无效输入,并且errno和结束指针可帮助您区分所有这些值。
如果在使用例如strtoll()读取值后需要转换为int,则可以对照中为int定义的范围来检查返回值(存储在long long中)的范围。 :INT_MIN和INT_MAX。
有关完整的详细信息,请参见以下答案:strtol()的正确用法。
请注意,这些功能都不会告诉您使用了哪种转换。您需要自己分析字符串。古怪的注意:您知道C源代码中没有十进制0吗?当您编写0时,您正在编写一个八进制常量(因为它的第一位是0)。这琐事没有实际的后果。
-
输入的值大于(或小于)将适合整数类型的值。返回值被限制在该类型支持的范围的末尾,但是errno == ERANGE表示发生了溢出。如果您具有32位long值,则即使使用了所有数字,也可能发生在值5,000,000,000(减去逗号)上的情况。