我有一个string变量,它可以是三件事中的一件:
在第1和第3种情况下,我不想做任何事情,而是传递数据。但是在第二种情况下,我需要把它转换成一个普通的数字。如果我总是简单地将变量转换为一个常规数字,那么当它包含实际文本时,它就变成了"0“。所以我需要知道字符串是否是一个科学符号中的数字。显而易见的,肮脏的答案是这样的算法:
只要看到数字,就遍历字符串。如果第一个遇到的字母是"e“或"E”,后面跟着"+“或"-",或者严格地说是更多的数字,那么它就是一个科学符号中的数字,否则它只是一个普通的数字或文本。
但是,我认为在C++98中有更好的方法来实现这一点(不需要boost)。有什么内置的方法可以帮忙吗?即使它只是使用尝试/捕捉。
编辑这个问题是因为它被认为是家庭作业。这不是作业。因此,应该重新开放。另外,为了澄清,由于技术上的限制,我不得不使用C++98。
我根据我最初的想法绘制了一个有限状态自动机("other“意味着没有为该给定状态指定的所有字符)。我相信这是正确的。
一些应该接受的示例输入:
1.453e-8
0.05843E5
8.43e6
5.2342E-7一些应该失败的示例输入:
hello
03HX_12
8432
8432E
e-8
fail-83e1

发布于 2016-05-11 23:44:31
您的自动机的主要问题在于规范/需求领域:它需要小数点两边的一个或多个数字,拒绝这样的输入:
.123
.123E3
123.
123.E+3并不是立即就应该拒绝这些形式;有些编程语言允许这些形式。不过,拒绝没有尾数数字的东西可能是个好主意,尽管如此:
.
.E+03如果这是错误的需求,而不是有意的,那么您的状态机必须进行调整。因为现在元素是可选的,所以修复状态机最简单的方法就是让它成为非确定性的(NFA图)。这只会带来不必要的困难。由于您最终将编写代码,所以使用特别的过程代码来处理这个问题是最容易的。
与常规自动机相比,特别过程代码的优点是向前看:它可以在不消耗输入的情况下查看下一个字符,就像自动机一样,它可以根据查找决定是否使用字符,而不需要在多个代码路径之间进行转换。换句话说,伪代码:
have_digits_flag = false
while (string begins with a digit character) {
have_digits_flag = true
consume digit character
}
if (!string begins with a decimal point)
goto bad;
consume decimal point
while (string begins with digit) {
consume digit
have_digits_flag = true;
}
if (!have_digits_flag)
goto bad; // we scanned just a decimal point not flanked by digits!
if (string begins with e or E) {
consume character
if (string begins with + or -)
consume character
if (!string begins with digit)
goto bad;
while (string begins with digit)
consume character
}
if (string is empty)
return true;
// oops, trailing junk
bad:
return false;显然,“字符串以”操作在字符串为空时必须是安全的。空字符串不满足任何字符的“字符串以”谓词的要求。
如何实现“消费字符”取决于您。您可以从std::string对象中实际删除一个字符,或者通过它移动一个迭代器,以指示输入位置。
这种方法的缺点是效率不高。这里可能并不重要,但通常情况下,特别代码会遇到这样的问题,即它不断地测试多个案例,以避免前瞻。在一个复杂的模式中,这些可能是很多的。表驱动的自动机从来不需要第二次查看任何输入符号。
发布于 2016-05-12 01:29:17
JSON使用相当好的状态机来解析数字。它不太严格,但不接受像"e“或"-.e2”这样的垃圾。
它是:
紧接着是
紧接着是
紧接着是
如果您希望看到更正式指定的格式,请参见RFC 7159,第6节。
数字=减去整数,小数点= %x2E;数字1-9 = %x31-39;1-9 e= %x65 / %x45;e- exp =e-/+1*数字框架=小数点1*数字int =0/(数字1-9*数字)减= %x2D;- plus = %x2B;+零= %x30;0
这就是我在JSON解析器中为数字所做的工作(我支持允许全范围64位整数的扩展格式,JSON规范说整数不能可靠地位于范围-2^53+1到2^53-1的范围之外):
template<typename InputIt,
typename V = typename
std::iterator_traits<InputIt>::value_type>
static InputIt extractNumber(Variant& result, InputIt st, InputIt en);template<typename InputIt, typename V>
InputIt JsonParser::extractNumber(Variant& result, InputIt st, InputIt en)
{
if (st == en)
parseError("Expected number at end of input");
std::vector<V> text;
auto accept = [&] {
if (st == en)
parseError("Expected number at end of input");
text.emplace_back(*st++);
};
// -?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?
// A B C D E
bool isFloatingPoint = false;
if (*st == '-')
{
// A
accept();
}
if (*st == '0')
{
// B
accept();
}
else if (std::isdigit(*st, cLocale))
{
// C
do
{
accept();
}
while (st != en && std::isdigit(*st, cLocale));
}
else
{
parseError("Invalid number");
}
if (st != en && *st == '.')
{
accept();
isFloatingPoint = true;
// D
while (st != en && std::isdigit(*st, cLocale))
accept();
}
if (st != en && (*st == 'E' || *st == 'e'))
{
isFloatingPoint = true;
// E
accept();
if (st != en && (*st == '+' || *st == '-'))
accept();
if (st == en || !std::isdigit(*st, cLocale))
parseError("Invalid number");
while (st != en && std::isdigit(*st, cLocale))
accept();
}
text.emplace_back(0);
if (isFloatingPoint)
result.assign(std::atof(text.data()));
else
result.assign(std::int64_t(std::atoll(text.data())));
return st;
}我不得不稍微调整一下,因为周围的实现确保了st在输入时不等于en,我最初断言。它通过了我的单元测试。
发布于 2016-05-12 02:59:21
bool is_valid(std::string src){
std::stringstream ss;
ss << src;
double d=0;
ss >> d;
if (ss){
return true;
}
else{
return false;
}
}我有个简单的解决办法。使用C++流来测试是字符串是一个数字。
https://stackoverflow.com/questions/37172819
复制相似问题