首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >测试C++98字符串是否为科学表示法中的数字

测试C++98字符串是否为科学表示法中的数字
EN

Stack Overflow用户
提问于 2016-05-11 20:37:46
回答 4查看 1K关注 0票数 4

我有一个string变量,它可以是三件事中的一件:

  1. 一个数字
  2. 科学符号中的数字
  3. 文本

在第1和第3种情况下,我不想做任何事情,而是传递数据。但是在第二种情况下,我需要把它转换成一个普通的数字。如果我总是简单地将变量转换为一个常规数字,那么当它包含实际文本时,它就变成了"0“。所以我需要知道字符串是否是一个科学符号中的数字。显而易见的,肮脏的答案是这样的算法:

只要看到数字,就遍历字符串。如果第一个遇到的字母是"e“或"E”,后面跟着"+“或"-",或者严格地说是更多的数字,那么它就是一个科学符号中的数字,否则它只是一个普通的数字或文本。

但是,我认为在C++98中有更好的方法来实现这一点(不需要boost)。有什么内置的方法可以帮忙吗?即使它只是使用尝试/捕捉。

编辑这个问题是因为它被认为是家庭作业。这不是作业。因此,应该重新开放。另外,为了澄清,由于技术上的限制,我不得不使用C++98。

我根据我最初的想法绘制了一个有限状态自动机("other“意味着没有为该给定状态指定的所有字符)。我相信这是正确的。

一些应该接受的示例输入:

代码语言:javascript
复制
1.453e-8
0.05843E5
8.43e6
5.2342E-7

一些应该失败的示例输入:

代码语言:javascript
复制
hello
03HX_12
8432
8432E
e-8
fail-83e1

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-05-11 23:44:31

您的自动机的主要问题在于规范/需求领域:它需要小数点两边的一个或多个数字,拒绝这样的输入:

代码语言:javascript
复制
.123
.123E3
123.
123.E+3

并不是立即就应该拒绝这些形式;有些编程语言允许这些形式。不过,拒绝没有尾数数字的东西可能是个好主意,尽管如此:

代码语言:javascript
复制
.
.E+03

如果这是错误的需求,而不是有意的,那么您的状态机必须进行调整。因为现在元素是可选的,所以修复状态机最简单的方法就是让它成为非确定性的(NFA图)。这只会带来不必要的困难。由于您最终将编写代码,所以使用特别的过程代码来处理这个问题是最容易的。

与常规自动机相比,特别过程代码的优点是向前看:它可以在不消耗输入的情况下查看下一个字符,就像自动机一样,它可以根据查找决定是否使用字符,而不需要在多个代码路径之间进行转换。换句话说,伪代码:

代码语言:javascript
复制
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对象中实际删除一个字符,或者通过它移动一个迭代器,以指示输入位置。

这种方法的缺点是效率不高。这里可能并不重要,但通常情况下,特别代码会遇到这样的问题,即它不断地测试多个案例,以避免前瞻。在一个复杂的模式中,这些可能是很多的。表驱动的自动机从来不需要第二次查看任何输入符号。

票数 3
EN

Stack Overflow用户

发布于 2016-05-12 01:29:17

JSON使用相当好的状态机来解析数字。它不太严格,但不接受像"e“或"-.e2”这样的垃圾。

它是:

  • "-“或者
  • 没什么,

紧接着是

  • “0”,或者,
  • (1位数字1-9,后面是0或0-9)

紧接着是

  • (“.”其次是一个或多个0-9),或者,
  • 没什么

紧接着是

  • ((“e”或“E”),接(“+”或“-”或无),后面是0或0-9),或,
  • 没什么

如果您希望看到更正式指定的格式,请参见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的范围之外):

代码语言:javascript
复制
template<typename InputIt,
         typename V = typename 
            std::iterator_traits<InputIt>::value_type>
static InputIt extractNumber(Variant& result, InputIt st, InputIt en);
代码语言:javascript
复制
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,我最初断言。它通过了我的单元测试。

票数 2
EN

Stack Overflow用户

发布于 2016-05-12 02:59:21

代码语言:javascript
复制
bool is_valid(std::string src){
    std::stringstream ss;
    ss << src;
    double d=0;
    ss >> d; 
    if (ss){
       return true;
    }
    else{
       return false;
    }
}

我有个简单的解决办法。使用C++流来测试是字符串是一个数字。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37172819

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档