注册 登录
编程论坛 VFP论坛

判断一个字符串是否纯数字,有无更优雅的算法?

cssnet 发布于 2022-09-27 09:28, 2639 次点击
遇一个函数,需将传入的字符串参数逐字拆解,判断是否纯十进制数字。
比方说:
"1021"、"238A"、"1314X520"……
最粗鲁的算法,无非For...Endfor循环,逐字判断是否数字。

人活世上,可不能要求那么低!——不能光吃饱了完事,还应有所追求吧。
除了眼前的苟且,我感觉吧,还应远眺一眼远方。
你说是吧?
那么,有没有更优雅一点点、让人眼前一亮、会心一笑的算法?
想了一个,自我感觉挺讨巧的,只可惜无法处理"0023"、"001"这种带前导“0”的情况:
*----------------------------------
* 拆解一个字符串参数,逐字判断是否纯十进制数字?
*----------------------------------
function isAllDigit
lpara lcString
if empty(lcString)
    return .F.
endif
lnLen = len(lcString)-1
lnDigit = val(lcString)
if lnDigit > 10 ^ lnLen
    return .T.
else
    return .F.
endif
*----------------------------------

敢问诸侠,砖我是抛出来了,有无玉可捡?
21 回复
#2
厨师王德榜2022-09-27 10:15
我觉得 是不是全数字,不能刻意的理解为这个字串中只能有0~9,比如
'-1006.001' / '+1006.001' 这样的字串,用val()转换后,数值不失真,
这种字串在我看来,仍是"全数字"的,所以 要允许字串中含有 "- + . " 符号.

同时, "+ -" 必须在最左,且不能同时出现 ,
      "." 可以在 最左/最右/中间,但是,必须只能出现一次.
总之,我对字串是不是全数字的判断标准,就是:用val()转换后,数值不失真.
有了上述规则,那么不难猜测,逐字符判断仍是最简单有效的办法.
在这个逐字符基础上想要提效,那就把正则用上.
以上就是我对此问题的理解,欢迎指正.
#3
cssnet2022-09-27 10:34
以下是引用厨师王德榜在2022-9-27 10:15:17的发言:
"全数字"要允许字串中含有 "- + . " 符号.
同时, "+ -" 必须在最左,且不能同时出现 ,
      "." 可以在 最左/最右/中间,但是,必须只能出现一次.
总之,我对字串是不是全数字的判断标准,就是:用val()转换后,数值不失真.


厨版确实是一只老谋深算的狐狸仙,事情考虑得周全,无一遗漏,小可真心佩服得紧哪!
哦,对了,厨版的鞋面有一点点尘,请让我给您擦一擦,好不好?


是这样的,这函数用途较特殊,判断是否纯数字,仅仅是用于给文件夹或文件命名——
不会有“+ - .”等情况,特别是文件名当中绝不允许出现“.”,扩展名才有“.”。
故而可算是“阉割版”的纯数字判断:只需匹配0--9。只需要找公公,不需要找小鸡鸡。
不过前导“0”的情况,则避无可避,必须精准处理。
#4
cssnet2022-09-27 10:55
当然,这问题纯属“中学生Basic语言竞赛题”那样无聊的“脑筋急转弯”,无非是希望设计一个偏门古怪算法:

不用循环,判断0--9纯数字

若用循环,那就不好玩啦。
呵呵。

我老人家琢磨了一下,将顶楼代码稍事改造一下,应能办到!
优雅、轻盈;
风流倜傥,玉树临风;
面如冠玉,貌胜潘安。

#5
cssnet2022-09-27 11:18

开古:

*----------------------------------
* 拆解一个字符串参数,逐字判断是否纯十进制数字?
* Ver 0.2
*----------------------------------
function isAllDigit
lpara lcString
if empty(lcString)
    return .F.
endif
if val("1"+lcString) >= 10 ^ len(lcString)
    return .T.
else
    return .F.
endif
*----------------------------------
#6
laowan0012022-09-27 13:52
此题吹版之前给过精简方法
if empty(CHRTRAN(lcString,'1234567890',''))
    messagebox(lcString+'是纯数字串')
endif

优雅否?

当然前导0还需另外处理一下下

[此贴子已经被作者于2022-9-27 13:58编辑过]

#7
cssnet2022-09-27 14:23
以下是引用laowan001在2022-9-27 13:52:26的发言:

此题吹版之前给过精简方法
if empty(CHRTRAN(lcString,'1234567890',''))
    messagebox(lcString+'是纯数字串')
endif


这一个函数可太妙啦!
而且不需考虑数值溢出。
我写的函数,其实有个整型值溢出的问题,须加个on error。
#8
吹水佬2022-09-27 17:15
VFP的字符串功能不弱,如果还有个字符串指针就更强大。
还是按字符来处理
程序代码:
? isAllDigit("1021")
? isAllDigit("238A")
? isAllDigit("1314X520")

FUNCTION isAllDigit(lcString)
    RETURN ALINES(arr,lcString,5,"0","1","2","3","4","5","6","7","8","9")==0
ENDFUNC
#9
cssnet2022-09-27 17:44
以下是引用吹水佬在2022-9-27 17:15:07的发言:

FUNCTION isAllDigit(lcString)
    RETURN ALINES(arr,lcString,5,"0","1","2","3","4","5","6","7","8","9")==0
ENDFUNC


这一个函数也非常巧妙!

不过,用空数组来搞,有些晦涩,乍一看,有些晕
另,不排除空字符串参数,这可能是个小漏洞。

? isAllDigit("")
#10
吹水佬2022-09-27 18:49
回复 9楼 cssnet
没考虑那么多,点到即止。
空串要另当别论,如果空串有另用,就要分别处理;无用就直接返回.f.
#11
吹水佬2022-09-27 21:26
VFP的字符串功能虽不弱,但也有限,实际应用还有不少考究。
#12
csyx2022-09-27 23:40
这一类的事情应该交给正则表达式去干,何必重新发明轮子。更重要的是,几乎所有语言都支持正则,以后不管你换啥语言都终身受用。

例如,达到你的需求:
程序代码:
oo = NewObject('vbscript.regexp')
oo.Pattern = '^\d+$'
? oo.test('1234')
? oo.test('a1234')

要实现二楼的带符号带小数判断,改下 pattern 就行
oo.Pattern = '^(\-?|\+?)(\d+)(\.?\d+)$'

要把 .1234 也算作合法,就这样
oo.Pattern = '^((\-?|\+?)(\d+)(\.?\d+)|(\.?\d+))$'

如果还要包含指数形式 1e234,也都可以通过只改匹配模板就轻易做到


[此贴子已经被作者于2022-9-28 03:16编辑过]

#13
schtg2022-09-28 06:26
向各位大侠学习!
#14
吹水佬2022-09-28 08:04
来个C货
程序代码:
DECLARE long strlen IN msvcrt string
DECLARE long sscanf IN msvcrt string,string,string@

? isAllDigit("1021","%[0-9]")
? isAllDigit("238A","%[0-9]")
? isAllDigit("1314X520","%[0-9]")
? isAllDigit("-123.456","%[0-9.-]")
? isAllDigit("-12.34+45.67","%[0-9.-+]")

FUNCTION isAllDigit(lcString,lcFmt)
    len = strlen(lcString)
    IF len==0
        RETURN .f.
    ENDIF
    buf = REPLICATE(0h00,len+1)
    sscanf(lcString,lcFmt,@buf)
    RETURN strlen(buf)==len
ENDFUNC
#15
cssnet2022-09-28 09:57
以下是引用csyx在2022-9-27 23:40:46的发言:
这一类的事情应该交给正则表达式去干,何必重新发明轮子。更重要的是,几乎所有语言都支持正则,以后不管你换啥语言都终身受用。


确实,在现代编程语言的世界里,正则是字符串检索的全宇宙标准,没有之一。
唯,楼主的需求本身就很小,最直观的解决方案,一个VFP循环即可搞定。
一来“贪玩”,二来考虑到执行效率,用VFP原生代码执行效率自然比较高。
诚如吹版所言:VFP的字符串处理并不弱,甚至强过了许多流行语言。
若非要用正则object来处理,颇有点儿“大炮打蚊子”意味。
呵呵。

Function isAllDigit()本身没有对错,有的只是算法上的巧与拙、妙与凡的小小差别而已。
或者这么说吧——两个字:
好玩!
#16
cssnet2022-09-28 09:59
以下是引用吹水佬在2022-9-28 08:04:13的发言:
来个C货
DECLARE long strlen IN msvcrt string
DECLARE long sscanf IN msvcrt string,string,string@


学到了,感谢!原来在msvcrt有宝藏啊,哈哈,看来要好好学习一下msvcrt.dll等库的参考手册。
#17
sam_jiang2022-09-28 16:26
人才
#18
schtg2022-09-29 06:30
学习,谢谢!
#19
吹水佬2022-09-29 07:29
以下是引用cssnet在2022-9-28 09:59:45的发言:



学到了,感谢!原来在msvcrt有宝藏啊,哈哈,看来要好好学习一下msvcrt.dll等库的参考手册。

微软的C函数库,会C函数编程就会用。
VFP也自带v7.1版的msvcr71.dll,VFP带来的应该要会用。
#20
easyppt2022-09-29 08:30
各位都是算法高手,佩服!
#21
plsword2022-11-15 16:52
这是我用来检测字串是否能用Val()精准转换数字的代码

PARAMETERS str
PRIVATE tmp
IF EMPTY(str)
    RETURN .t.
ENDIF
tmp=OCCURS("-",ALLTRIM(str))
DO CASE
    CASE tmp>1
        RETURN .f.
    CASE tmp=1
        IF LEFT(ALLTRIM(str),1)<>"-"
            RETURN .f.
        ENDIF
ENDCASE
tmp=OCCURS("+",ALLTRIM(str))
DO CASE
    CASE tmp>1
        RETURN .f.
    CASE tmp=1
        IF LEFT(ALLTRIM(str),1)<>"+"
            RETURN .f.
        ENDIF
ENDCASE
IF OCCURS(".",ALLTRIM(str))>1
    RETURN .f.
ENDIF
IF OCCURS(" ",ALLTRIM(str))>0
    RETURN .f.
ENDIF
IF EMPTY(CHRTRAN(ALLTRIM(str), '+|-|.|0|1|2|3|4|5|6|7|8|9', ''))
    RETURN .t.
ELSE
    RETURN .f.
ENDIF
#22
sdta2022-11-15 17:51
chrtran()如何?
1