第20题:表示数值的字符串
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
测试用例:
Test("Test1", "100", true);
Test("Test2", "123.45e+6", true);
Test("Test3", "+500", true);
Test("Test4", "5e2", true);
Test("Test5", "3.1416", true);
Test("Test6", "600.", true);
Test("Test7", "-.123", true);
Test("Test8", "-1E-16", true);
Test("Test9", "1.79769313486232E+308", true);
printf("\n\n");
Test("Test10", "12e", false);
Test("Test11", "1a3.14", false);
Test("Test12", "1+23", false);
Test("Test13", "1.2.3", false);
Test("Test14", "+-5", false);
Test("Test15", "12e+5.4", false);
Test("Test16", ".", false);
Test("Test17", ".e1", false);
Test("Test18", "e1", false);
Test("Test19", "+.", false);
Test("Test20", "", false);
Test("Test21", nullptr, false);
思路:
1.形如A[.[B]][e|EC]或.[B][e|EC]的都来表示数值,其中AC为有符号整型,B为无符号整型
2.小数点前后,只要有一个整型即可,且小数点后的整型是无符号整型。
3.指数前后必须要都有有符号整型。
4.字符串里不能有除了0-9的其它字符。
第一遍
// 整数的格式可以用[+|-]B表示, 其中B为无符号整数
bool scanInteger(const char** str)
{
//1.如果前面有[+|-]号,往后移一位
if (**str == '+' || **str == '-')
(*str)++;//注意这里(*str) 要打括号 ++比*优先级高。
//2.返回无整形扫描
return scanUnsignedInteger(str);
}
bool scanUnsignedInteger(const char** str)
{
//1.定义before指针,指向str第一个字符
const char* before = *str;
//2.如果*str不为空,且是数字的话,进行下一个字符
while (*str&&**str >= '0'&&**str <= '9')
(*str)++;
//3.如果有数字字符的话,就返回真,否则返回假
return before < *str ? true : false;
}
// 数字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示,其中A和C都是
// 整数(可以有正负号,也可以没有),而B是一个无符号整数
bool isNumeric(const char* str)
{
//1.如果为空
if (!str)
return false;
//2.判断第一个字符是否是数值,A
bool numeric = scanInteger(&str);
//3.如果遇到小数点 . 用||,因为小数点有三种情况,1.1/0.1/1.0
if (*str == '.')
{
//3.1 str++成为左值,因为要取地址
str++;
//3.2 细节:不用return 更新numeric的值
numeric= scanUnsignedInteger(&str) || numeric ;//细节:先扫描小数位B,是否为unsigned,因为如果numeric==ture就不会扫描小数位了
}
//4.如果遇到指数e|E ,用&&,因为指数前后必须都要是数字, e2,1e是错的
if (*str == 'e' || *str == 'E')
{
str++;
numeric= numeric&&scanInteger(&str);
}
//5.返回是否为数值numeric,且已经扫描到了'\0'
return numeric&&*str=='\0';
}
bug
1.左值:问题描述:
想给一个指向int型的指针赋值,以下为代码,就会在第三行报错“表达式必须为左值或者函数操作符”int *P; int a; p=&(a+2);
改写成以下代码就不会报错了
int *P; int a,b; b=a+2; p=&b;
问题分析:
分析一下为什么第一种写法就是错的, 究其原因就是取地址操作符&必须作用于一个左值,而(a+2)明显是一个表达式,不是一个合法的左值,当然要报错了。左值与右值这两概念是从 c 中传承而来的,在 c 中,左值既能够出现在等号左边也能出现在等号右边,右值则是只能出现在等号右边。右值就是一个临时变量(没有在程序中申明),只有临时的地址空间,左值是在程序中具体定义了的,有其地址空间。换句话说,使用取地址符&对某个值取地址,左值能够得到地址,而右值不能得到地址!
2.||的顺序
//3.2 细节:不用return 更新numeric的值
numeric= scanUnsignedInteger(&str) || numeric ;//细节:先扫描小数位B,是否为unsigned,因为如果numeric==ture就不会扫描小数位了
3.函数返回值,不能直接返回numeric,还要检查是否到了字符串末尾
//5.返回是否为数值numeric,且已经扫描到了'\0'
return numeric&&*str=='\0';
第二遍
class Solution {
public:
//形如A[.[B]][e|EC]或.[B][e|EC]的都来表示数值,其中AC为有符号整型,B为无符号整型
//小数点前后,只要有一个整型即可,且小数点后的整型是无符号整型。
//指数前后必须要都有有符号整型。
//字符串里不能有除了0-9的其它字符。
bool scanUnsignedInt(const char** str)
{
//1.定义before指针char*保存原来的str
const char* before = *str;
//2.如果**str不为结束符,**str的值在0-9,继续往下指
while(**str!='\0'&&**str>='0'&&**str<='9')
(*str)++;
//3.比较*str和before的位置,判断是否存在数字,返回
return before<*str?true:false;
}
bool scanInt(const char** str)
{
//1.如果有正负号,str往后指一个
if(**str=='+' || **str == '-')
(*str)++;
//2.返回扫描无符号整数
return scanUnsignedInt(str);
}
bool isNumeric(const char* string)
{
//1.判断是否为nullptr
if(!string)
return false;
//2.定义标志numeric,表达是否为数字,首先扫描带符号的数值
bool numeric=scanInt( &string);
//3. 如果遇到小数,小数点后的B应该为无符号整形,小数点前后只要有一个数字就行,所以numeric用||重新赋值,
//其中||的顺序是:scanUnsigned函数在前,因为numeric为真,就不会判断scanUnsigned函数的值了
if(*string=='.')
{
string++;
numeric=scanUnsignedInt(&string)||numeric;
}
//4.如果遇到指数,指数的前后都应该有数字,所以用&&
if(*string=='e'||*string=='E')
{
string++;
numeric=scanInt(&string)&&numeric;
}
//5.返回numeric,且string扫描到了结束符
return numeric&&*string=='\0';
}
};
网友的
class Solution {
public:
bool isNumeric(char* str) {
// 标记符号、小数点、e是否出现过
bool sign = false, decimal = false, hasE = false;
for (int i = 0; i < strlen(str); i++) {
if (str[i] == 'e' || str[i] == 'E') {
if (i == strlen(str)-1) return false; // e后面一定要接数字
if (hasE) return false; // 不能同时存在两个e
hasE = true;
} else if (str[i] == '+' || str[i] == '-') {
// 第二次出现+-符号,则必须紧接在e之后
if (sign && str[i-1] != 'e' && str[i-1] != 'E') return false;
// 第一次出现+-符号,且不是在字符串开头,则也必须紧接在e之后
if (!sign && i > 0 && str[i-1] != 'e' && str[i-1] != 'E') return false;
sign = true;
} else if (str[i] == '.') {
// e后面不能接小数点,小数点不能出现两次
if (hasE || decimal) return false;
decimal = true;
} else if (str[i] < '0' || str[i] > '9') // 不合法字符
return false;
}
return true;
}
};
编译原理中自动机(你是魔鬼吗?
class Solution {
public:
bool isNumeric(char* string)
{
int i = 0;
if(string[i]=='+' || string[i]=='-' || IsNum(string[i])){
while(string[++i]!='\0' && IsNum(string[i]));
if(string[i]=='.'){
if(IsNum(string[++i])){
while(string[++i]!='\0' && IsNum(string[i]));
if(string[i]=='e'||string[i]=='E'){
i++;
if(string[i]=='+' || string[i]=='-' || IsNum(string[i])){
while(string[++i]!='\0' && IsNum(string[i]));
if(string[i]=='\0') return true;
else return false;
}else return false;
}else if(string[i]=='\0') return true;
else return false;
}else if(string[++i]=='\0') return true;
else return false;
}else if(string[i]=='e'||string[i]=='E'){
i++;
if(string[i]=='+' || string[i]=='-' || IsNum(string[i])){
while(string[++i]!='\0' && IsNum(string[i]));
if(string[i]=='\0') return true;
else return false;
}else return false;
}else if(string[i]=='\0') return true;
else return false;
}else return false;
}
bool IsNum(char ch)
{
if(ch<'0'||ch>'9') return false;
else return true;
}
};
正则表达式(魔鬼
//正则表达式解法
public class Solution {
public boolean isNumeric(char[] str) {
String string = String.valueOf(str);
return string.matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");
}
}
/*
以下对正则进行解释:
[\\+\\-]? -> 正或负符号出现与否
\\d* -> 整数部分是否出现,如-.34 或 +3.34均符合
(\\.\\d+)? -> 如果出现小数点,那么小数点后面必须有数字;
否则一起不出现
([eE][\\+\\-]?\\d+)? -> 如果存在指数部分,那么e或E肯定出现,+或-可以不出现,
紧接着必须跟着整数;或者整个部分都不出现
*/
//参见剑指offer
public class Solution {
private int index = 0;
public boolean isNumeric(char[] str) {
if (str.length < 1)
return false;
boolean flag = scanInteger(str);
if (index < str.length && str[index] == '.') {
index++;
flag = scanUnsignedInteger(str) || flag;
}
if (index < str.length && (str[index] == 'E' || str[index] == 'e')) {
index++;
flag = flag && scanInteger(str);
}
return flag && index == str.length;
}
private boolean scanInteger(char[] str) {
if (index < str.length && (str[index] == '+' || str[index] == '-') )
index++;
return scanUnsignedInteger(str);
}
private boolean scanUnsignedInteger(char[] str) {
int start = index;
while (index < str.length && str[index] >= '0' && str[index] <= '9')
index++;
return start < index; //是否存在整数
}
}