有效数判定 Valid Number

时间:2022-02-15 15:50:30

问题:给出一个字符串,判断它是否是一个有效的数字。

什么叫有效的数字呢?

整数 ,小数 "2.5",正数 "+25",负数"-25",科学计数法"-2e1+0"。

特殊用例:

“.5”, “5.” 认为是有效数字。

无效用例:

“.", "e25", "25e", "2e5e5", "2.5.5", "2e2.5"

输入检查:左右两端出现空格是允许的,内部不能有空格。

方法1:依据上面所能想到的无效用例,处理零碎的细节。

方法2:将零碎的细节用有限状态自动机来表示。

方法3:偷懒直接用strtod函数。


方法1:处理零碎的细节。

class Solution {
public:
bool isNumber(const char *s) {
if(s == NULL)
return false;

int ex, dot;
ex = dot = 0; // only once

while(*s == ' ') // space
s++;

if(*s == '+' || *s == '-') // sign
s++;

if(isNum(*s)) // first char is numeric
s++;
else if(*s == '.') // if first char is '.'
{
s++;
dot = 1;
if(isNum(*s)) // then second char has to be numeric
s++;
else
return false;
}
else
return false;

while(*s != '\0')
{
if(*s == '.')
{
if(ex == 1 || dot == 1)
return false;
dot = 1;
}
else if(*s == 'e')
{
if(ex == 1)
return false;
ex = 1;
if(*(s+1) == '+' || *(s+1) == '-') //sign for exponent
s++;
if(!isNum(*(s+1))) // numeric after 'e'
return false;
}
else if(*s == ' ') // space has to be on the right side
{
break;
}
else if(!isNum(*s)) //other letter
{
return false;
}

s++;
}
while(*s == ' ') // space
s++;
if(*s == '\0')
return true;
else
return false;
}

bool isNum(const char c)
{
return (c >= '0' && c <= '9'); // 0<=c<=9
}
};

方法2:有限状态自动机。

对于这个思想,最重要的是构建状态转换矩阵。其中,每一行代表一种状态,每一列代表使状态转移的一种输入。
可能的输入有6种:无效字符0、空格1、正负号2、数字3点4、e5。
可能的状态有8种: -1 错误态,任何状态接收到无效字符都会进入错误态,一旦进入错误态,状态就不会再转换下去。 0  初始态,可接受空格、正负号、数字、点。 1  接收到正负号而来,可接受数字、点。 2  由状态3接收到点而来,可接受数字、空格、e。 3  由状态0/1/3接收到数字而来,可接受数字、e、空格、点。 4  由状态2/3/9接收到数字而来,可接受数字、e、空格。 5  由状态2/3/4接收到e而来,可接受正负号、数字。 6  由状态5接收到正负号而来,可接受数字。 7  由状态5/6接收到数字而来,可接受数字、空格。 8  由状态2/3/4/7接收到空格而来,可接受空格。 9  由状态0/1接收到点而来,可接受数字。
状态0是初始状态,状态2、3、4、7、8可作为终止状态。
根据状态和输入,构造状态转换矩阵。

{
     -1, 0, 1, 3, 9, -1, //状态0:""
     -1, -1,-1, 3, 9, -1, //状态1:"+"
     -1, 8, -1, 4, -1, 5, //状态2:"2."
     -1, 8, -1, 3, 2, 5, //状态3:"2"
     -1, 8, -1, 4, -1, 5, //状态4:"2.3"
     -1, -1, 6, 7, -1, -1, //状态5:"2.3e"
     -1, -1, -1, 7, -1, -1, //状态6:"2.3e+"
     -1, 8, -1, 7, -1, -1, //状态7:"2.3e+4"
     -1, 8, -1, -1, -1, -1, //状态8:"2.3e+4 "
     -1, -1, -1, 4, -1, -1 //状态9:"."
};

#include <iostream>
#include <stdio.h>
using namespace std;

class Solution {
public:
enum InputType
{
INVALID, //0
SPACE, //1
SIGN, //2
DIGIT, //3
DOT, //4
EXPONENT, //5
};

bool isNumber(const char *s) {
if(s == NULL)
return false;

const int transitionTable[][6] =
{
-1, 0, 1, 3, 9, -1, //状态0:""
-1, -1,-1, 3, 9, -1, //状态1:"+"
-1, 8, -1, 4, -1, 5, //状态2:"2."
-1, 8, -1, 3, 2, 5, //状态3:"2"
-1, 8, -1, 4, -1, 5, //状态4:"2.3"
-1, -1, 6, 7, -1, -1, //状态5:"2.3e"
-1, -1, -1, 7, -1, -1, //状态6:"2.3e+"
-1, 8, -1, 7, -1, -1, //状态7:"2.3e+4"
-1, 8, -1, -1, -1, -1, //状态8:"2.3e+4 "
-1, -1, -1, 4, -1, -1 //状态9:"."
};
int state = 0;
InputType input;
while(*s != 0)
{
if(*s == ' ')
input = SPACE;
else if(*s == '+' || *s == '-')
input = SIGN;
else if(*s == '.')
input = DOT;
else if(*s == 'e' || *s == 'E')
input = EXPONENT;
else if(*s >= '0' && *s <= '9')
input = DIGIT;
else
input = INVALID;

state = transitionTable[state][input];
if(state == -1)
return false;
++s;
//cout<<state<<" ";
}
//cout<<endl;
return (state == 2 || state == 3 || state == 4 || state == 7 || state == 8);
}
};

int main()
{
Solution s;
char str[100];
bool r;
while(scanf("%s", str) != EOF)
{
r = s.isNumber(str);
cout<<"result :"<<r<<endl;
}
return 1;
}

方法3:偷懒。

double strtod (const char* str, char** endptr);

将字符串形式的浮点数转化为double类型。 
参数一,str为输入字符串。
参数二,二重指针,函数返回后,endptr会指向str中第一个浮点数之后的位置。
返回值,转化成的double浮点数。

class Solution {
public:
bool isNumber(const char *s) {
char *left;
strtod(s, &left);
if(left == s)
return false;
while(*left != 0)
{
if(*left != ' ')
return false;
++left;
}
return true;
}
};