`strtod("3ex", &end)` 的结果应该是啥? `sscanf` 呢?
Posted
技术标签:
【中文标题】`strtod("3ex", &end)` 的结果应该是啥? `sscanf` 呢?【英文标题】:What is the result of `strtod("3ex", &end)` supposed to be? What about `sscanf`?`strtod("3ex", &end)` 的结果应该是什么? `sscanf` 呢? 【发布时间】:2014-12-07 16:30:43 【问题描述】:在我的实验中这个表达式
double d = strtod("3ex", &end);
用3.0
初始化d
,并将end
指针放在输入字符串中的'e'
字符处。这正是我期望它的行为。 'e'
字符可能看起来是指数部分的开头,但由于缺少实际的指数值(6.4.4.2 要求),因此 'e'
应被视为完全独立的字符。
但是,当我这样做时
double d;
char c;
sscanf("3ex", "%lf%c", &d, &c);
我注意到sscanf
使用'3'
和'e'
的%lf
格式说明符。变量d
接收3.0
值。变量c
以'x'
结束。这对我来说看起来很奇怪,原因有两个。
首先,由于语言规范在描述%f
格式说明符的行为时引用了strtod
,我直观地期望%lf
以与strtod
相同的方式处理输入(即选择与终止点)。但是,我知道从历史上看,scanf
应该不超过一个字符返回输入流。这限制了任何前瞻scanf
可以由一个字符执行的距离。上面的例子需要至少两个字符的前瞻。所以,假设我接受 %lf
消耗了输入流中的 '3'
和 'e'
的事实。
但是我们遇到了第二个问题。现在sscanf
必须将"3e"
转换为类型double
。 "3e"
不是浮点常量的有效表示(同样,根据 6.4.4.2,指数值不是可选的)。我希望sscanf
将此输入视为错误:在%lf
转换期间终止,返回0
并保持d
和c
不变。但是,上述sscanf
成功完成(返回2
)。
这种行为在标准库的 GCC 和 MSVC 实现之间是一致的。
所以,我的问题是,在 C 语言标准文档中,它究竟允许sscanf
的行为如上所述,参考以上两点:消耗超过strtod
并成功转换为@ 等序列987654360@?
通过查看我的实验结果,我可能可以“逆向工程”sscanf
的行为:消耗尽可能多的“看起来正确”,从不后退,然后将消耗的序列传递给 strtod
。这样'e'
被%lf
消耗,然后被strtod
忽略。但是语言规范中的所有内容都是这样吗?
【问题讨论】:
@HighPredator: OP 可能意味着变量c
应该达到值'e'
而不是值'x'
。或者它根本不应该获得任何值,并且函数 sscanf
应该返回 1 而不是 2(因此它准确地模拟了 strtod
的行为)。
@HighPredator:我实际上描述了我在问题中遇到的两个问题。我一直直观地期望sscanf
格式要求和行为与strto...
格式要求和行为同步。语言标准实际上说明了这一点,但显然我在其中看到了更多的东西。例如,我预计 sscanf
会在 strto...
停止的同一点停止。现在我有点“看到”标准可能不需要这样做,并允许sscanf
消耗更多。
虽然您观察到的行为看起来有点奇怪,但 sscanf
和 strtod
并不要求表现出相似(或等效)的行为。 strto.
*scanf()
需要从左到右扫描。但是strtod()
可能会“向前看”并决定将 endptr 放在哪里。
@Blue Moon:是的,但是语言规范通过简单地引用strtod
来定义f
格式说明符的行为。如果f
说明符和strtod
之间存在差异,则标准应在某处对其进行描述。我的问题是:在哪里?具体是哪个措辞?
一个有趣的重复案例——与其说是 question,不如说是 answer:Difference between scanf() and strtol() / strtod() in parsing numbers 基本上,...scanf()
被定义为取最长的可能序列,即或者是匹配输入的前缀,而strto...()
取最长的有效序列。 (区别在于流只支持一个保证回退的字符,即...scanf()
不能像strto...()
那样回退。)
【参考方案1】:
我只是在die.net找到下面的描述
strtod()、strtof() 和 strtold() 函数将初始 nptr 指向的字符串的一部分为 double、float 和 long 分别表示双重表示。
字符串(初始部分)的预期形式是可选的 isspace(3) 识别的前导空白,可选加号 ('+') 或减号 ('-'),然后是 (i) 十进制数,或 (ii) 十六进制数,或 (iii) 无穷大,或 (iv) NAN (不是数字)。
十进制数由十进制数字的非空序列组成 可能包含一个基数字符(小数点, 取决于语言环境,通常是 '.'),可选地后跟一个小数 指数。十进制指数由一个“E”或“e”组成,后跟一个 可选的加号或减号,后跟一个非空序列 十进制数字,表示乘以 10 的幂。
十六进制数由“0x”或“0X”后跟非空字符组成 可能包含基数字符的十六进制数字序列, 可选地后跟一个二进制指数。二进制指数包括 'P' 或 'p',后跟可选的加号或减号,后跟 由十进制数字的非空序列,并表示乘法 2 的幂。基数字符和二进制指数中的至少一个 必须在场。
无论大小写,无穷大要么是“INF”,要么是“INFINITY”。
NAN 是“NAN”(不考虑大小写)可选地后跟 '(', a 字符序列,后跟')'。字符串 以与实现相关的方式指定 NAN 的类型。
然后我做了一个实验,我用gcc执行了下面的代码
#include <stdlib.h>
#include <stdio.h>
char head[1024], *tail;
void core(const char *stmt)
sprintf(head, "%s", stmt);
double d=strtod(head, &tail);
printf("cover %s to %.2f with length=%ld.\n", head, d, tail-head);
int main()
core("3.0x");
core("3e");
core("3ex");
core("3e0x");
return 0;
并得到结果
cover 3.0x to 3.00 with length=3.
cover 3e to 3.00 with length=1.
cover 3ex to 3.00 with length=1.
cover 3e0x to 3.00 with length=3.
所以,'e'后面似乎应该有一些数字。
对于sscanf
,我用gcc代码做了另一个实验:
#include <stdlib.h>
#include <stdio.h>
char head[1024];
void core(const char *stmt)
int i;sscanf(stmt, "%x%s", &i, head);
printf("sscanf %s catch %d with '%s'.\n", stmt, i, head);
int main()
core("0");
core("0x0g");
core("0x1g");
core("0xg");
return 0;
然后得到下面的输出:
sscanf 0 catch 0 with ''.
sscanf 0x0g catch 0 with 'g'.
sscanf 0x1g catch 1 with 'g'.
sscanf 0xg catch 0 with 'g'.
似乎 sscanf 会尝试捕获更多字符,并且如果它判断它当前是合法的,则不会回滚(可能在不完整的情况下是非法的)。
【讨论】:
以上是关于`strtod("3ex", &end)` 的结果应该是啥? `sscanf` 呢?的主要内容,如果未能解决你的问题,请参考以下文章
c++中如何将字符串类似于s="1 2 3 4 5 6 7 8 9"转换成整型数组a=1,2,3,4,5,6,7,8,9,