parse a string

BlitzMax Forums/BlitzMax Beginners Area/parse a string

Hi !

a$ = "v=1.0,a=100,t=120,a=200,t=180,r=200,t=30,r=200"

i've the previous string and want parse it as follow :

when i read 'a = 100' i want execute the function acceleration(100), when i read t=120 the function turn (120), ...

before i begin to code this could you advert me on the best algo and usefull blitzmax functions to use ?

Thanks !

I'd split the string into separate instructions, and then split the instruction into the function and the value. You could code your own function to split strings or use this one from the code archives. As seyhajin said, BlitzMax has these functions already built-in...

For example:

SuperStrict

'Let's test this function with the sample string you posted on the site. NB: I've not added all functions so some won't be understood...

ProcessInstructionsFromString( "v=1.0,a=100,t=120,a=200,t=180,r=200,t=30,r=200" )

'~~~~~~~~~~~~~~~
'Useful Function
'~~~~~~~~~~~~~~~

Function ProcessInstructionsFromString( pString$ )
	
	'Let's remove all spaces from the string, and then split up the instructions
	Local strInstructions$[] = pString.Replace(" ","").Split(",")
	
	'Now, we can process each instruction one at a time
	For Local tmpInstruction$ = EachIn strInstructions
		
		'For each instruction, we want to split it to retrieve the function (index: 0) and the value (index: 1)
		Local tmpParts$[] = tmpInstruction.Split("=")
		
		'Let's make sure that there was an equals in the instruction, and that there was a value (index: 1) 
		If tmpParts.length <> 2 Or Not tmpParts[1].length Then
			Print "Error: Cannot parse instruction: ~q" + tmpInstruction + "~q"
			Continue
		EndIf
		
		'Next, let's find out what function we should be using
		Select tmpParts[0].ToLower$()
			
			'Pass the value (index: 1) to the function we want by casting it as a float
			Case "a";accelerate(Float(tmpParts[1]))
			Case "t";turn(Float(tmpParts[1]))
			
			'Otherwise, let us know that the function specified is not understood
			Default;Print "Error: I do not understand the instruction ~q" + tmpParts[0] + "~q"
			
		EndSelect
		
	Next

EndFunction

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'Test functions that are called
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Function accelerate( pAcceleration# )

	Print "Accelerating at a rate of ~q" + pAcceleration + "~q"

EndFunction

Function turn( pAngle# )

	Print "Turning to ~q" + pAngle + "~q degrees"

EndFunction


You can add as many functions as you want to interpret, simply by adding another Case to the Select block.

Edit: I've added a few comments to let you know what's happening.

Edit 2: Updated so that it uses the built-in BlitzMax string methods.

Search the code archives for String Tokenizer.

Many thanks for your help. I'll use this code into my game editor. So the editor user could describe/add some paths to the ennemies ships.

Concept code :
Strict

Const CTE_VITESSE  = 1
Const CTE_DEPLACER = 2
Const CTE_TOURNER  = 3
Const CTE_PAUSE    = 4
Const CTE_VITESSE_ROTATION = 5
Const CTE_ARRETER  = 6

' --------------------------------------------------------------------------------------------------	
	
Type Tpile

	Field Code_action
	Field Parametre
	
	Function Ajouter:TPile (Code_action, Parametre)
	
		Local p:TPile = New Tpile
		p.Code_action = Code_action
		p.Parametre = Parametre
		Return p
		
	End Function
	
End Type

' --------------------------------------------------------------------------------------------------

Type TVecteur

	Field Liste_action : TList = CreateList()
	Field Link_liste_action:TLink = Liste_action.FirstLink()
	
	Field bAction_terminee
	Field bEnPause

	Field PosX    : Float
	Field PosY    : Float
	Field Angle   : Float
	Field Vitesse : Float
	
	Field angle_cible : Float
	Field Vitesse_rotation : Float
	
	Field Deplacement_index
	Field Deplacement_max
	Field Deplacement_sens
	
	Field TempsPause : Int
	Field Temps      : Int
	
	Field x_dep#
	Field y_dep#
	
	Field Code_action

	'-----------------------------------------------------------------------------------------------
	
	Function Creer : Tvecteur (PosX#, PosY#)
		Local v:TVecteur = New TVecteur
		
		v.PosX# = PosX#
		v.PosY# = PosY#
		v.Angle = 0
		v.Vitesse# = 0.0
		v.Vitesse_rotation# = 0.0
		v.Deplacement_index = 0
		v.Deplacement_max = 0
		v.bAction_terminee = True
		v.bEnPause = False
		
		Return v
		
	End Function

	'-----------------------------------------------------------------------------------------------
	
	Method Ajouter_action (Code_action, pParametre)
		Local a:TPile
		a = TPile.Ajouter (Code_action, pParametre)
		Link_liste_action = Liste_action.FirstLink() 
		ListAddLast Liste_action, a
	End Method

	'-----------------------------------------------------------------------------------------------
	
	Method Donner_prochaine_action:TPile()
		
		Local a:TPile
		
		If TPile(Link_liste_action.value()) <> Null Then
			a.Code_action = TPile(Link_liste_action.value() ).code_action
			a.Parametre = TPile(Link_liste_action.value() ).parametre
		Else
			Return Null
		End If
		
		If Link_liste_action.NextLink() <> Null Then
			Link_liste_action = Link_liste_action.NextLink()
		Else 
			Return Null
		End If 
		
		Return a

	End Method

	'-----------------------------------------------------------------------------------------------
	
	Method Gerer_actions()
	
		Local a:TPile
		If bAction_terminee = True Then

			a = Donner_prochaine_action()

			If a.Code_action<>Null Then
			
				Select a.Code_action
				
					Case CTE_VITESSE
						FixerVitesse (a.Parametre)
						bAction_terminee = False
						Code_action = a.Code_action
					
					Case CTE_DEPLACER
					
						Deplacer (a.Parametre)
						bAction_terminee = False
						Code_action = a.Code_action
						
					Case CTE_TOURNER
						Tourner (a.Parametre)
						bAction_terminee = False
						Code_action = a.Code_action
						
					Case CTE_PAUSE
						Pause (a.Parametre)
						bAction_Terminee = False
						Code_action = a.Code_action

					Case CTE_VITESSE_ROTATION
						FixerVitesse_rotation (a.Parametre)
						bAction_Terminee = False
						Code_action = a.Code_action
						
						
					Case CTE_ARRETER
						Arreter()
						bAction_terminee = False
						Code_action = a.Code_action
				
				End Select
				
			End If
		End If
	
	End Method

	'-----------------------------------------------------------------------------------------------

	Method Deplacer (pNombre_de_points)
		
		If pNombre_de_points > 0 Then 
			Deplacement_Sens = 1
		End If
		
		If pNombre_de_points < 0 Then 
			Deplacement_Sens = - 1
		End If

		Deplacement_Max = Abs (pNombre_de_points)

	End Method

	'-----------------------------------------------------------------------------------------------

	Method FixerVitesse_rotation (pVitesse#)
		Vitesse_rotation# = pVitesse#
	End Method 	

	'-----------------------------------------------------------------------------------------------
	
	Method FixerVitesse (pVitesse#)
		Vitesse# = pVitesse#
	End Method 

	'-----------------------------------------------------------------------------------------------
	
	Method Tourner (pAngle)
		Angle_Cible = pAngle
	End Method

	'-----------------------------------------------------------------------------------------------
	
	Method Pause (pTempsPause)
		TempsPause = pTempsPause
	End Method 

	'-----------------------------------------------------------------------------------------------
	
	Method Arreter ()
	End Method 

	'-----------------------------------------------------------------------------------------------
	
	Method Calculer()

		Select Code_action

			Case CTE_VITESSE
					bAction_terminee = True
		
			Case CTE_DEPLACER
			
				
				If deplacement_index >= deplacement_max Then
					deplacement_index = 0
					x_dep# = 0
					y_dep# = 0
					bAction_terminee = True
				Else
					deplacement_index = deplacement_index + 1
					x_dep# =  Cos (Angle) * Vitesse#
					y_dep# =  Sin (Angle) * Vitesse#
				End If

				Select deplacement_sens
				
					Case 1
					 posx# = posx# + x_dep#
					 posy# = posy# + y_dep#
					Case -1
					 posx# = posx# - x_dep#
					 posy# = posy# - y_dep#
				
				End Select 
				
			Case CTE_PAUSE
			
				If bEnPause = False And bAction_terminee = False Then
					Temps = MilliSecs()
					bEnPause = True
				End If
			
				If Temps + TempsPause < MilliSecs() Then 
					bEnPause = False
					bAction_terminee = True
					Temps = 0
					TempsPause = 0
				End If
					
			Case CTE_TOURNER
			
				' deviation progressive
				
				Local TE# = RotaryDir( angle# , angle_cible#)
						
				If TE# < 0 Then angle# :- Vitesse_rotation#
				If TE# > 0 Then angle# :+ Vitesse_rotation#
				'
				While angle# > 360 angle#:-360 Wend
				While angle# <= 0 angle#:+360 Wend		
				
				angle = Int (angle)			

				x_dep# =  Cos (Angle#) * Vitesse#
				y_dep# =  Sin (Angle#) * Vitesse#
				
				Select deplacement_sens
				
					Case 1
					 posx# = posx# + x_dep#
					 posy# = posy# + y_dep#
					Case -1
					 posx# = posx# - x_dep#
					 posy# = posy# - y_dep#
				
				End Select 				
				
				If Angle# = Angle_cible# Then bAction_terminee = True
				
			Case CTE_VITESSE_ROTATION
				bAction_terminee = True
			
			Case CTE_ARRETER
			
				x_dep# = 0
				y_dep# = 0
				bAction_terminee = True
			
		End Select

	End Method

	'-----------------------------------------------------------------------------------------------
	
	Method Afficher()
	
		DrawOval posx#-3.0, posy#-3.0,6,6
	
	End Method
	
	' Fonction split Noel Cower's dans le code archive du site officiel
	'-----------------------------------------------------------------------------------------------
	
	Function SplitString$[]( s$, sp$ )
	    Local p:Int, l:Int, o$[32], x%
	    Local n:Int
	    For n = 0 Until s.length
	        Local lx% = x
	        For p = 0 Until sp.length
	            If s[n] = sp[p] Then
	                If x = o.length Then o = o[..o.length*2]
	                o[x] = s[l..n+(n = s.Length-1 And (Not (s[n]=sp[p])))].Trim( )
	                l = n+1
	                If o[x].length=0 Then Exit
	                x :+ 1
	                Exit
	            EndIf
	        Next
	        If x <> lx Then Continue
	        If n = s.length-1 Then
	            If x = o.length Then o = o[..o.length*2]
	            o[x] = s[l..n+(n = s.length-1 And (Not (s[n]=sp[p])))].Trim( )
	            If o[x].length = 0 Then Exit
	            x :+ 1
	        EndIf
	    Next
	    If x = 0 Then Return Null
	    Return o[0..x]
	End Function
	
	' Parser codé par SebHoll (http://www.blitzbasic.com/Community/posts.php?topic=72190)
	'-----------------------------------------------------------------------------------------------
	
	Method ProcessInstructionsFromString( pString$ )
	
		'Let's remove all spaces from the string, and then split up the instructions
		Local strInstructions$[] = SplitString(pString.Replace(" ",""),",")
		
		'Now, we can process each instruction one at a time
		For Local tmpInstruction$ = EachIn strInstructions
			
			'For each instruction, we want to split it to retrieve the function (index: 0) and the value (index: 1)
			Local tmpParts$[] = SplitString(tmpInstruction,"=")
			
			'Let's make sure that there was an equals in the instruction, and that there was a value (index: 1) 
			If tmpParts.length <> 2 Or Not tmpParts[1].length Then
				Print "Error: Cannot parse instruction: ~q" + tmpInstruction + "~q"
				Continue
			EndIf
			
			'Next, let's find out what function we should be using
			Select tmpParts[0].ToLower$()
				
				'Pass the value (index: 1) to the function we want by casting it as a float
				Case "ro";Ajouter_action(CTE_VITESSE_ROTATION, Float(tmpParts[1]))
				Case "v";Ajouter_action(CTE_VITESSE, Float(tmpParts[1]))
				Case "a";Ajouter_action(CTE_DEPLACER, Float(tmpParts[1]))
				Case "r";Ajouter_action(CTE_DEPLACER, - Float(tmpParts[1]))
				Case "t";AJouter_action(CTE_TOURNER, Float(tmpParts[1]))
				Case "p";Ajouter_action(CTE_PAUSE, Int(tmpParts[1]))
				Case "s";Ajouter_action(CTE_ARRETER, Float(tmpParts[1]))
				
				'Otherwise, let us know that the function specified is not understood
				Default;Print "Error: I do not understand the instruction ~q" + tmpParts[0] + "~q"
		
			EndSelect
			
		Next


	End Method	
	
	' Function trouvée sur le forum officiel 
	' (http://www.blitzbasic.com/Community/posts.php?topic=57317#637149)
	'-----------------------------------------------------------------------------------------------
	
	Function RotaryDir#(SourceDir#,DestDir#)
	
		Local Diff1#,Diff2#,Dir#
	
		If SourceDir# > DestDir#
			Diff1#=SourceDir-DestDir
			diff2#=(360.0-SourceDir)+DestDir
			If diff2<diff1
				dir#=diff2
			Else
				dir#=diff1/-1
			EndIf
		Else
			If SourceDir#<DestDir#
				diff1=DestDir-SourceDir
				diff2=(360.0-DestDir)+SourceDir
				If diff2<diff1
					dir#=diff2/-1
				Else
					dir#=diff1
				EndIf
			Else
				dir=0
			EndIf
		EndIf
		Return dir
		
	End Function	
	
End Type

' --------------------------------------------------------------------------------------------------

' Programme principal

' --------------------------------------------------------------------------------------------------

' v : set speed, param# = speed
' ro : set rotation, ro# = rotation factor
' a : advance, param = pixels 
' r : rear, param = pixels
' t : turn, param = angle
' p : pause, param = millisecs
' s=0 put this at the end.



Local v1:TVecteur = TVecteur.Creer (100,100)
v1.ProcessInstructionsFromString( "v=2.0,ro=6.0,a=100,t=120,a=200,p=2000,t=180,r=200,t=30,r=200,s=0" )

Local v2:TVecteur = TVecteur.Creer (400,400)
v2.ProcessInstructionsFromString( "v=1.0,ro=2.0,a=100,t=020,a=300,t=80,a=100,p=3000,t=130,r=100,s=0" )


Graphics 1024,768

SetBlend alphablend

While Not KeyDown (KEY_ESCAPE)
	'Cls
	v1.Gerer_actions()
	v2.Gerer_actions()
	v1.Calculer()
	v2.Calculer()
	SetColor 255,0,0
	v1.Afficher()
	SetColor 0,255,0
	v2.Afficher()
	Flip 
Wend


You have hard links to functions there, how about using the event system and just send out an event and have functions that listen to that event? That way you could add more commands without ever needing to update that Select-case-EndSelect part again.

This seems to be a good idea but i'm not familiar with the event system. So i don't know where to start to implement this !

SuperStrict

Type Ttest
	Field a:Int=1
	Field b:Int=2
	Field c:Float=3.5

	Field newevent:TEvent=New TEvent
	
	Function eventhook:Object(id:Int,data:Object,context:Object)
		If Ttest(context) Ttest(context).ev TEvent(data);Return data	
	EndFunction
	
	Method New()
		AddHook EmitEventHook,eventhook,Self
	End Method
	
	Method Free()
		RemoveHook EmitEventHook,eventhook
		GCCollect()
	End Method
	
	Method ev(event:TEvent)
		If event.id=$999666
			newevent.id=$999555
			newevent.extra=Self
			EmitEvent newevent
		EndIf
	End Method
	
End Type

Function test2:Object(id:Int,data:Object,context:Object)
	Local ev:TEvent=TEvent(data)
	
	If ev.id=$999555
		Local p:Ttest=Ttest(ev.extra)
	
		DebugLog p.a
		DebugLog p.b
		DebugLog p.c
	EndIf
	
	Return data
EndFunction

AddHook EmitEventHook,test2

Local t:Ttest=New Ttest

Local a:TEvent=New TEvent

a.id=$999666
EmitEvent a
End


Some older code from me, from a topic elsewhere. Study it, it shows you how to send a type to a function through an event.

Yowzer, I don't trust events to arrive regularly or in order, but perhaps I'm just suspicious. I'd much rather have control that hand it over to Window's Events!

it's an interesting approach, nice reading, but as Grey say. i prefer stay on my basic solution.

Yowzer, I don't trust events to arrive regularly or in order, but perhaps I'm just suspicious.

Got any proof of that?? I figured the whole OS works event-based already, and most people would prefer event-based programming because of its flexibility and non-hardwired'ness.

And because it stops sucking out notebook accu cells like mainloop non-delay based applications, ie it follows the guidelines of Microsoft, Apple and Intel (don't know about AMD, but most likely they suggest events as well)

BRL have integrated two new no documented String Method : Split and Join

Prototypes :
Method Split:String[]( separator$ )
Method Join:String( bits:String[] )

Example of use :
Local s$ = "Hello world"

Local ssplit$[] = s.split(" ")

For Local t$ = EachIn ssplit
	Print t$
Next

Local j$
Print j.Join(ssplit)


Hey hub. I wrote up a solution for you and posted it in the code archive.

Grab it here:

http://www.blitzbasic.com/codearcs/codearcs.php?code=2101

Many thanks for this !

BRL have integrated two new no documented String Method : Split and Join

Prototypes :
Method Split:String[]( separator$ )
Method Join:String( bits:String[] )


I didn't know that!!! Thanks for pointing them out - just another example of how poorly documented BlitzMax is. :-(


BRL have integrated two new no documented String Method : Split and Join

Prototypes :
Method Split:String[]( separator$ )
Method Join:String( bits:String[] )


Doh!
When? Damn....

During 1.24 through syncmod

SuperStrict

Local d:String = ","
Local joined:String = d.join(["apple","cherry","pear"])
Print joined

For Local s:String = EachIn joined.split(",")
	Print s
Next


duh, better...
Local joined:String = ",".join(["apple","cherry","pear"])


Local s$ = "Hello|world|This is|a waste of time|For|Sure"

Print Tokenize(s$,1,"|")
Print Tokenize(s$,4,"|")

Function Tokenize:String(Inp:String,Number:Int,Separator:String)
	Local Split:String[] = Inp.split(Separator)
	Local Find:Int=1
	
	For Local T:String = EachIn Split
		If Number=Find Then
			Split=Null
			Return T
		EndIf
		
		Find:+1
	Next
	
	Split=Null
	Return ""
End Function


Local s$ = "Hello|world|This is|a waste of time|For|Sure"
Local t$[] = s.split("|")
Print t[0]
Print t[3]


Tks Fredborg :)