C++ 程序行为
C++ program behavior
演示了当输入后有附加字符时,C++ 无法检测浮点数中的输入问题。
234.5 在使用 cin 时工作正常,但 234.t(演示输入时手指滑动)会读取 234. 并保留 t.
想象一下当某些输入失败而其他输入失败时我的惊讶。注意:这些并不是复杂的验证器,只是一个向学生介绍输入可能难以验证的演示。
在 C++ 14 下的 Clion Mac 上编译的程序
#include <iostream>
#include <fstream>
int main() {
float number = 0.0;
int counter = 0;
std::ifstream inData;
inData.open("input.txt");
inData >> number;
for(int i = 0; i < 26;i++ ){
if(inData.good()) {
std::cout << "Good Row number: " << i << " " << static_cast<char>(i+97) << std::endl;
} else {
std::cout << "\tFail Row number: " << i << " " << static_cast<char>(i+97) << std::endl;
inData.clear();
inData.ignore(1, '\n');
}
inData >> number;
}
return 0;
}
注意:出于好奇,我运行使用这个输入文件
234.a
234.b
234.c
234.d
234.e
234.f
234.g
234.h
234.i
234.j
234.k
234.l
234.m
234.n
234.o
234.p
234.q
234.r
234.s
234.t
234.u
234.v
234.w
234.x
234.y
234.z
这是输出:
Fail Row number: 0 a
Fail Row number: 1 b
Fail Row number: 2 c
Fail Row number: 3 d
Fail Row number: 4 e
Fail Row number: 5 f
Good Row number: 6 g
Fail Row number: 7 h
Good Row number: 8 i
Fail Row number: 9 j
Fail Row number: 10 k
Good Row number: 11 l
Fail Row number: 12 m
Good Row number: 13 n
Fail Row number: 14 o
Good Row number: 15 p
Fail Row number: 16 q
Good Row number: 17 r
Fail Row number: 18 s
Fail Row number: 19 t
Good Row number: 20 u
Fail Row number: 21 v
Fail Row number: 22 w
Good Row number: 23 x
Fail Row number: 24 y
Good Row number: 25 z
有人知道发生了什么事吗?
评论说明:
-是的,使用 'a' 与 97 相比更好,但部分课程是强调字符具有十进制值,因此它们很容易比较。
- 提供
- 针对核心问题进行了简化。稍后我会处理第二部分。
这似乎是解析浮点数的不一致之一,偶尔会出现在标准库的实现中(参见 1169, 2381)。
考虑以下修改后的代码段
#include <iostream>
#include <sstream>
#include <string>
int main()
{
float number{};
std::string test{ "234.0" };
for(char ch{'a'}; ch <= 'z'; ++ch )
{
test.back() = ch;
std::istringstream ss{ test };
if (ss >> number)
std::cout << ch << ' ';
}
std::cout << '\n';
}
Here 你可以看到,当使用 Clang 和 -stdlib=libc++
编译时,它会产生以下输出
g h j k l m o q r s t u v w y z
而 -stdlib=libstdc++
给出
a b c d f g h i j k l m n o p q r s t u v w x y z // Only 'e' is rejected
使用 strtof
, all the cases are rejected (see e.g. here).
(5) Extracts a floating-point value potentially skipping preceding whitespace. The value is stored to a given reference value.
This function behaves as a FormattedInputFunction. After constructing and checking the sentry object, which may skip leading whitespace, extracts a floating-point value by calling std::num_get::get().
标准指定 std::num_get::get()
在 28.4.3.2 的行为
[locale.num.get].
分为三个阶段
- 确定转换说明符
- 从 in 中提取字符并为阶段 1 中确定的转换规范所期望的格式确定相应的 char 值。
- 存储结果
stage 2的细节提到
If it is not discarded, then a check is made to determine if c is allowed as the next character of an input field of the conversion specifier returned by Stage 1.
If so, it is accumulated.
在 stage 3
The sequence of chars accumulated in stage 2 (the field) is converted to a numeric value by the rules of one of the functions declared in the header :
[...]
For a float value, the function strtof.
[...]
The numeric value to be stored can be one of:
- zero, if the conversion function does not convert the entire field.
- [...]
- the converted value, otherwise.
The resultant numeric value is stored in val.
If the conversion function does not convert the entire field, or if the field represents a value outside the range of representable values, ios_base::failbit
is assigned to err
.
根据之前的结果,似乎阶段 2 在两个测试库中都没有正确实现。
演示了当输入后有附加字符时,C++ 无法检测浮点数中的输入问题。
234.5 在使用 cin 时工作正常,但 234.t(演示输入时手指滑动)会读取 234. 并保留 t.
想象一下当某些输入失败而其他输入失败时我的惊讶。注意:这些并不是复杂的验证器,只是一个向学生介绍输入可能难以验证的演示。
在 C++ 14 下的 Clion Mac 上编译的程序
#include <iostream>
#include <fstream>
int main() {
float number = 0.0;
int counter = 0;
std::ifstream inData;
inData.open("input.txt");
inData >> number;
for(int i = 0; i < 26;i++ ){
if(inData.good()) {
std::cout << "Good Row number: " << i << " " << static_cast<char>(i+97) << std::endl;
} else {
std::cout << "\tFail Row number: " << i << " " << static_cast<char>(i+97) << std::endl;
inData.clear();
inData.ignore(1, '\n');
}
inData >> number;
}
return 0;
}
注意:出于好奇,我运行使用这个输入文件
234.a
234.b
234.c
234.d
234.e
234.f
234.g
234.h
234.i
234.j
234.k
234.l
234.m
234.n
234.o
234.p
234.q
234.r
234.s
234.t
234.u
234.v
234.w
234.x
234.y
234.z
这是输出:
Fail Row number: 0 a
Fail Row number: 1 b
Fail Row number: 2 c
Fail Row number: 3 d
Fail Row number: 4 e
Fail Row number: 5 f
Good Row number: 6 g
Fail Row number: 7 h
Good Row number: 8 i
Fail Row number: 9 j
Fail Row number: 10 k
Good Row number: 11 l
Fail Row number: 12 m
Good Row number: 13 n
Fail Row number: 14 o
Good Row number: 15 p
Fail Row number: 16 q
Good Row number: 17 r
Fail Row number: 18 s
Fail Row number: 19 t
Good Row number: 20 u
Fail Row number: 21 v
Fail Row number: 22 w
Good Row number: 23 x
Fail Row number: 24 y
Good Row number: 25 z
有人知道发生了什么事吗?
评论说明: -是的,使用 'a' 与 97 相比更好,但部分课程是强调字符具有十进制值,因此它们很容易比较。
- 提供
- 针对核心问题进行了简化。稍后我会处理第二部分。
这似乎是解析浮点数的不一致之一,偶尔会出现在标准库的实现中(参见 1169, 2381)。
考虑以下修改后的代码段
#include <iostream>
#include <sstream>
#include <string>
int main()
{
float number{};
std::string test{ "234.0" };
for(char ch{'a'}; ch <= 'z'; ++ch )
{
test.back() = ch;
std::istringstream ss{ test };
if (ss >> number)
std::cout << ch << ' ';
}
std::cout << '\n';
}
Here 你可以看到,当使用 Clang 和 -stdlib=libc++
编译时,它会产生以下输出
g h j k l m o q r s t u v w y z
而 -stdlib=libstdc++
给出
a b c d f g h i j k l m n o p q r s t u v w x y z // Only 'e' is rejected
使用 strtof
, all the cases are rejected (see e.g. here).
(5) Extracts a floating-point value potentially skipping preceding whitespace. The value is stored to a given reference value. This function behaves as a FormattedInputFunction. After constructing and checking the sentry object, which may skip leading whitespace, extracts a floating-point value by calling std::num_get::get().
标准指定 std::num_get::get()
在 28.4.3.2 的行为
[locale.num.get].
分为三个阶段
- 确定转换说明符
- 从 in 中提取字符并为阶段 1 中确定的转换规范所期望的格式确定相应的 char 值。
- 存储结果
stage 2的细节提到
If it is not discarded, then a check is made to determine if c is allowed as the next character of an input field of the conversion specifier returned by Stage 1. If so, it is accumulated.
在 stage 3
The sequence of chars accumulated in stage 2 (the field) is converted to a numeric value by the rules of one of the functions declared in the header :
[...]
For a float value, the function strtof.
[...]
The numeric value to be stored can be one of:
- zero, if the conversion function does not convert the entire field.
- [...]
- the converted value, otherwise.
The resultant numeric value is stored in val.
If the conversion function does not convert the entire field, or if the field represents a value outside the range of representable values,ios_base::failbit
is assigned toerr
.
根据之前的结果,似乎阶段 2 在两个测试库中都没有正确实现。