Detecting if a String is an Int

Monkey Forums/Monkey Programming/Detecting if a String is an Int

Fred(Posted 2013) [#1]
This trick to determine if a string is an Int works well with HTML5 but not with GLFW because v = 0.

Is the only way for GLFW a IsNumber function that checks all characters ? (+-.0123456789)

local param:String = "not_a_number"
local v:Int = Int( param )
local chk:String = String(v)
if chk <> "NaN"			
  ' param is a number
else
  ' param is a text
endif			



ziggy(Posted 2013) [#2]
If you ask me, it shold not work in HTML5 neither, as it is using the JavaScript NaN.
In my honest opinion, to detect if a string is a number, the best way (cross platform) would be to parse its contents with a function.


Fred(Posted 2013) [#3]
Thanks,
so, here is one, is there a better way ?
May be there is a string fonction that may avoid the loop ?
Function IsInt:Bool( str:String )
  local l:Int = str.Length()
  if l = 0 return false
  local c:Int
  for local n:Int = 0 until l
    c = str[n]
    if not (c = 43 or c = 45 or (c >= 48 and c <= 57))    ' - + 0 1 2 3 4 5 6 7 8 9
      return false
    endif		
  next
  return true
End



Fred(Posted 2013) [#4]
oh, it should check if + or - are only at the first position... repost later


ziggy(Posted 2013) [#5]
also the dot


Fred(Posted 2013) [#6]
:) for IsInt ?!

then:
Function IsInt:Bool( str:String )
  local l:Int = str.Length()
  if l = 0 return false
  local c:Int	= str[0]
  if not (c = 43 or c = 45 or (c >= 48 and c <= 57))    ' - + 0 1 2 3 4 5 6 7 8 9
    return false
  else
    if c = 43 or c = 45
      if l = 1	return false	' :) we never know
    endif
  endif
  for local n:Int = 1 until l
    c = str[n]
    if not (c >= 48 and c <= 57)    ' 0 1 2 3 4 5 6 7 8 9
      return false
    endif		
  next
  return true
End

Function IsFloat:Bool( str:String )
  local l:Int = str.Length()
  if l = 0 return false
  local c:Int	= str[0]
  if not (c = 43 or c = 45 or c = 46 or (c >= 48 and c <= 57))    ' - + . 0 1 2 3 4 5 6 7 8 9
    return false
  else
    if c = 43 or c = 45		' - +
      if l = 1	return false	' :) we never know
    endif
  endif
  for local n:Int = 1 until l
    c = str[n]
    if not ( c = 46 or (c >= 48 and c <= 57))    ' . 0 1 2 3 4 5 6 7 8 9
      return false
    endif		
  next
  return true
End


Is a dot at the last position makes a number a float ?

Playing with this (my former needs are very simple as the input text can only be written from myself, it's a config text file) it makes me wonder what are all the standard ways of writing numbers... and the best (funniest?) way of checking them...


ElectricBoogaloo(Posted 2013) [#7]
?

	Method isInt(ftxt$)
		Return Abs(Float(ftxt))>0 Or ftxt="0"
	End



ziggy(Posted 2013) [#8]
ElectricBoogaloo: Explicit conversion of wrong formated strings produces a crash on java based targets such as Android, and sometimes gives a NaN on JavaScript. That's why a parser is required. (Also, being very strict, 00, -0, +0, 0.0, .0, and even . are numbers too).

that's my take on it (I find it slightly simpler, but this may be just me):
Function IsInt:Bool(str:String)
	
	For Local i:Int = 0 Until str.Length
	  	Local char:= str[i]
		'If it's a number from 0 to 9:
		If char >= "0"[0] And char <= "9"[0]
			Continue
	
		'Else if it's a unary operator
		ElseIf i = 0 And (char = "+"[0] or char = "-"[0]) And str.Length > 1
			Continue
	
		'anything else makes the str not a number.
		Else
			Return False
		EndIf
	Next
	Return str.Length > 0	'True, unless the string is empty
End


Function IsFloat:Bool(str:String)
	
	Local hasDot:Bool = False
	
	For Local i:Int = 0 Until str.Length
  		Local char:= str[i]

		'If it's a number from 0 to 9:
		If char >= "0"[0] And char <= "9"[0]
			Continue

		'Else if it's a unary operator placed at the first char position
		ElseIf i = 0 And (char = "+"[0] or char = "-"[0]) And str.Length > 1
			Continue
			
		'else if it's a dot and there are no more dots in the string:
		ElseIf char = "."[0] And hasDot = False
			hasDot = True
			Continue
			
		'anything else makes the str not a number.
		Else
			Return False
		EndIf
  	Next
	Return str.Length > 0	'True, unless the string is empty
End


NOTE: Notice that things like:
"+"[0]
contains the integer representation of the "+" character, and this is solved at compile time by trans, so it's fast, and it avoids the creation of any arrays. Additionally, it is much more expressive in the source code than placing the constant for that specific char.

So comparing something like:
if char = "A"[0]

Is the same as:
if char = 65

But with "A"[0] you know wich character you're comparing with, while the 65 constant may be harder to follow unless the source code reader knows the mapping of chars. Also, it's not impossible to see character mapping changing in different targets, so using constants here looks a bit dangerous to me (maybe not in the US-latin area, but there are more characters and languages in the world).


ElectricBoogaloo(Posted 2013) [#9]
Sorry, didn't realise there were alt-target issues with that. Hmm. To be honest, I've barely touched anything user-input related, yet. Everything's been internal, so far. I should, really, start to tackle some sort of proper level editing and stuff, but .. *shrugs* I'm having fun with limits, at the minute!

I you're having to scan numbers, don't forget that some countries use commas in place of decimal points. ..and then there's the times when we insert commas to split the figures up, too. You'll probably need to account for both of those scenarios.


ziggy(Posted 2013) [#10]
I you're having to scan numbers, don't forget that some countries use commas in place of decimal points. ..and then there's the times when we insert commas to split the figures up, too. You'll probably need to account for both of those scenarios.

Yes, I know this. Also, those functions should ignore white space and tabs at the begining and at the end of the string, (without creating a substring to have better performance).
I just wasn't going that far, but feel free to improve it and share if you want!
I did have some issues with string to number conversion in an Android App, and at the end I add my own numbers only on screen keyboard, but having a realiable num-to-string conversion is a good thing. This could be a starting point?


Fred(Posted 2013) [#11]
"+"[0] I always forget this, this is more accurate than ascii code (even if it will be the same for a long time)(but my config file is utf8) and definitely easier to read.

@ElectricBougaloo that's what I meant :) but now it should be not platform dependant.

@ziggy the just one dot flag is a nice find, but checking i = 0 for every char in the loop is hurting me :) (a lot, counting cycles is so far)

About float representation
my monkey (from its jungle) tells me:
local floatvalue:Float = .
Syntax error - expecting identifier.

then to make your elegant function fits this constraint

'else if it's a dot and there are no more dots in the string:
ElseIf char = "."[0] And hasDot = False

turns to

ElseIf char = "."[0] And hasDot = False And str.Length > 1


Fred(Posted 2013) [#12]
@ElectricBoogaloo, cross post, you're unlikely right, (and I do know this '.' / ',' issue as I'm French), (and use a lot ',' to separate values...)

and the spaces and tabs... , we are then back to an ethernal question, my needs versus the "does everything" machine...

I'll use the last ziggy's IsInt()


Fred(Posted 2013) [#13]
local floatvalue:Float = Float( " +. hello nightmares... " ) ' " " are spaces and tabs (not shown here)
Print floatvalue

' html5: 0
' android: 0.0
' glfw: 0.0

local floatvalue:Float = Float( " .42 hello nightmares... " )

' html5: 0.42
' android: 0.42
' glfw (always a funny guy): 0.41999998688697815


ziggy(Posted 2013) [#14]
but checking i = 0 for every char in the loop is hurting me
If you mean logically, then maybe, but if you mean performance wise, it should not, comparing an integer is absurdly fast in all targets, and also notice that the And operator is shortcircuiting (just in case you did not know it).


Fred(Posted 2013) [#15]
That was a joke, trying to find something not perfect in your function just for the challenge. I was refering to my old memories when, back in 1979, writing ASM code, every cycle counted.

the And operator that does not execute the rest of the if line is something to know and tell.

Monkey: If ObjectRef And ObjectRef.value is safe in any language.

memories again: if( ptrobj && ptrobj->value ) :)
(personnal thought: I wondered why I'm refering a lot to these old stuff those days, I guess it's because I'm porting PopCorn(1988, 100% ASM) to Monkey... :)
)


Paul - Taiphoz(Posted 2013) [#16]
That is the first time I have seen that Print "B"[0] is that in the doc's ? also makes me wonder about any other cool little short cuts that might be in there that I don't know about.