gpt4 book ai didi

c++ - stringstream 无符号输入验证

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:29:57 25 4
gpt4 key购买 nike

我正在编写程序的一部分,用于解析和验证程序控制台参数中的一些用户输入。为此,我选择使用 stringstream,但遇到了读取无符号类型的问题。

下一个模板用于从给定字符串中读取请求的类型:

#include <iostream>
#include <sstream>
#include <string>

using std::string;
using std::stringstream;
using std::cout;
using std::endl;

template<typename ValueType>
ValueType read_value(string s)
{
stringstream ss(s);
ValueType res;
ss >> res;
if (ss.fail() or not ss.eof())
throw string("Bad argument: ") + s;
return res;
}
// +template specializations for strings, etc.

int main(void)
{
cout << read_value<unsigned int>("-10") << endl;
}

如果类型是无符号的并且输入字符串包含负数,我希望看到异常抛出(由 ss.fail() = true 引起)。但是 stringstream 生成转换为无符号类型的值(书面示例中为 4294967286)。

如何修复此示例以实现所需的行为(最好不要回退到 c 函数)?我知道这可以通过简单的第一个符号检查来完成,但我可以放置前导空格。我可以编写自己的解析器,但不相信问题如此不可预测,标准库无法解决。

隐藏在无符号类型字符串流运算符深处的函数是 strtoull 和 strtoul。它们以描述的方式工作,但提到的功能是低级的。为什么 stringstream 不提供一些验证级别? (我只希望我是错的,它确实是错的,但需要一些 Action 才能实现)。

最佳答案

版本免责声明:C++03 的答案不同。以下内容涉及 C++11。

首先,让我们分析一下发生了什么。

ss >> res;这称为 std::istream::operator>>(unsigned) .在[istream.formatted.arithmetic]/1中,effects定义如下:

These extractors behave as formatted input functions (as described in 27.7.2.2.1). After a sentry object is constructed, the conversion occurs as if performed by the following code fragment:

typedef num_get< charT,istreambuf_iterator<charT,traits> > numget;
iostate err = iostate::goodbit;
use_facet< numget >(loc).get(*this, 0, *this, err, val);
setstate(err);

In the above fragment, loc stands for the private member of the basic_ios class.

跟随格式化输入函数到[istream::sentry],sentry的主要作用这里的目标是消耗前导空白字符。它还可以防止在出现错误时执行上面显示的代码(流处于失败/eof 状态)。

使用的语言环境是 "C"语言环境。理由:

对于stringstream通过 stringstream ss(s); 构建,该 iostream 的语言环境是构建时的当前全局语言环境(这在 [ios.base.locales]/4 的兔子洞深处得到保证)。由于 OP 程序中的全局区域设置未更改,因此 [locale.cons]/2 指定“经典”区域设置,即 "C"语言环境。

use_facet< numget >(loc).get使用成员函数 num_get<char>::get(iter_type in, iter_type end, ios_base&, ios_base::iostate& err, unsigned int& v) const;在 [locale.num.get] 中指定(注意 unsigned int ,一切都还好)。字符串的详细信息 -> unsigned int “C”语言环境的转换很长,在 [facet.num.get.virtuals] 中有描述。一些有趣的细节:

  • 对于无符号整数值,函数strtoull被使用。
  • 如果转换失败,ios_base::failbit分配给 err .具体来说:“要存储的数值可以是以下之一:[...] 最负的可表示值或无符号整数类型的零,如果该字段表示一个太大的负值而无法在 val 中表示。ios_base::failbit分配给 err 。”

strtoull的定义需要到C99, 7.20.1.4 , 在第 5 段下:

If the subject sequence begins with a minus sign, the value resulting from the conversion is negated (in the return type).

第 8 段:

If the correct value is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type and sign of the value, if any), and the value of the macro ERANGE is stored in errno

对于strotoul,负值是否被视为有效输入似乎在过去一直存在争议。 .无论如何,问题出在这个函数上。对 gcc 的快速检查表明它被认为是有效输入,因此您观察到的行为也是如此。


历史记录:C++03

使用 C++03 scanfnum_get里面转换。不幸的是,我不太确定(还)如何转换为 scanf是指定的,在什么情况下会发生错误。


显式错误检查:

我们可以通过使用带符号的值进行转换和测试来手动插入该支票 <0 ,或者我们寻找 -字符(这不是一个好主意,因为可能存在本地化问题)。

关于c++ - stringstream 无符号输入验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18917167/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com