Get data between delimiters starting from a certain point.
What I am trying to accomplish is a simple function that I can call to extract a decimal value from a string right after the "search value".
I have a byte array (BYTE buffer[255]) that can look like this : R:C:03:V:12: D:7231:# (Ignore space between ":" and "D") What I would like to do is call this new function by using Value := extract_data(@buffer, "C") Variable "Value" is a LONG. When parsed properly, the number 3 will be the new value. Same would go if using : Value := extract_data(@buffer, "D") which would return 7231 as the new value.
There is a string method I found on the forums and have tried to make it work, but no go
Any help is greatly appreciated!
I have a byte array (BYTE buffer[255]) that can look like this : R:C:03:V:12: D:7231:# (Ignore space between ":" and "D") What I would like to do is call this new function by using Value := extract_data(@buffer, "C") Variable "Value" is a LONG. When parsed properly, the number 3 will be the new value. Same would go if using : Value := extract_data(@buffer, "D") which would return 7231 as the new value.
There is a string method I found on the forums and have tried to make it work, but no go

Comments
PUB extract_data(ptr, key, delim) | t, i, n[?] result := false t := 0 i := 0 repeat if byte[ptr][t++] == key repeat i +=1 until byte[ptr][t+ i] == delim bytemove(@n, ptr + t, i) byte[@n] [i + 1] := 0 return num.toStr(n, num#dec)*edit, forgot delimiters*
*aedit*
This should be something like you want?
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 ' Serial Terminal Stuff DEBUG_BAUD = 9_600 DEBUG_HEADING_X = 1 ' Output formatting data DEBUG_HEADING_Y = 1 DEBUG_DYNAMIC_X = DEBUG_HEADING_X + 10 DEBUG_DYNAMIC_Y = DEBUG_HEADING_Y + 2 VAR BYTE buffer[255] LONG value1 OBJ Pst : "Parallax Serial Terminal" num : "Numbers" PUB go NUM.init pause(1000) Pst.Start(DEBUG_BAUD) '1 Cog Pst.Clear Pst.Position(DEBUG_HEADING_X, DEBUG_HEADING_Y) Pst.Str(string("Current Position", 13)) Pst.PositionX(DEBUG_HEADING_X) Pst.Str(string("------------------------", 13)) Pst.PositionX(DEBUG_HEADING_X) Pst.Str(string("Input ................", 13)) Pst.PositionX(DEBUG_HEADING_X) Pst.Str(string("Value ................", 13)) repeat Update_Debug_Window pst.StrIn(@buffer) value1 := extract_data(buffer, STRING("C"), STRING(":")) 'value1 := 12 PUB Update_Debug_Window Pst.Position(DEBUG_DYNAMIC_X, DEBUG_DYNAMIC_Y) Pst.Str(@buffer) Pst.Str(string("...")) Pst.Position(DEBUG_DYNAMIC_X, DEBUG_DYNAMIC_Y + 1) Pst.Dec(value1) Pst.Str(string("...")) return PUB extract_data(ptr, key, delim) | t, i, n[255] result := false t := 0 i := 0 repeat if byte[ptr][t++] == key repeat i +=1 until byte[ptr][t+ i] == delim bytemove(@n, ptr + t, i) byte[@n] [i + 1] := 0 return num.toStr(n, num#dec) PUB pause(Duration) if(Duration > 0) waitcnt((clkfreq / 1_000 * Duration) + cnt) returnWhen I type in the PST input : R:C:43:G I am trying to get "43" to show up as Value.
Note * : n[?] showed up as an error.
You need to make the ? the max size of your string (the longest value you expect to return) + 1 (for zero delimiter) / 4 (bytes to long)
Now, I should have used num.FromStr
Other than that, I'd have to actually prototype it. I was just hoping to give a jumping off point. I think there are several errors at this point. I'll try to revisit it when I have a few extra minutes..
*edit*
I think this is closer,..
PUB extract_data(ptr, key) | t, i, n[4] result := false ' might change this repeat t from 0 to 255 if byte[ptr][t] == key t += 1 repeat i from 0 to 16 ' max string size if byte[ptr][t + i] == ":" bytemove(@n, ptr + t, i) byte[@n] [i + 1] := 0 return num.toStr(n, num#dec)The first repeat loop (t) looks for the key. Increment t to point at first byte of value, then the second repeat loop looks for the delimiter. Then copy to a temporary variable, null terminate the string and pass it to numbers. (Max number string size would be 16 bytes in this example.
Additionally, JonnyMac provided some real good insight to parsing at this post http://forums.parallax.com/showthread.php/159947-HELP.spin-I-Am-Going-In-Circles-And-Getting-Dizzy-Byte-Array?p=1313935&viewfull=1#post1313935 and then expands his his discussion in Post #9 of that thread, and then further expansion of the same concept, is made in the thread of the first link provided.
You should now be well armed, with several methods
Bruce
I added an ELSE to the extract_data IF statement and apparently I am calling this function incorrectly because it always hits the ELSE....
Attempts at calling have been :
value1 := extract_data(@buffer, "C", ":")
value1 := extract_data(buffer, "C", ":")
value1 := extract_data(@buffer, string("C"), string(":"))
value1 := extract_data(buffer, string("C"), string(":"))
None have worked....
PUB extract_data(ptr, key) | t, i, n[4] result := false ' might change this repeat t from 0 to 255 if byte[ptr][t] == key t += 1 repeat i from 0 to 16 ' max string size if byte[ptr][t + i] == ":" bytemove(@n, ptr + t, i) byte[@n] [i + 1] := 0 return num.FromStr(@n, num#dec)num.fromStr(@n, num#dec)
And it should increment t by 2...
PUB extract_data(ptr, key) | t, i, n[4] result := false ' might change this repeat t from 0 to 255 if byte[ptr][t] == key t+= 2 ' point at start of number repeat i from 0 to 16 ' max string size if byte[ptr][t + i] == delim bytemove(@n, ptr + t, i) byte[@n] [i + 1] := 0 return num.FromStr(@n, num#dec)*edit*
Fixed a couple mistakes
byte[@n] [i + 1] := 0
as
n.byte [i + 1] := 0
Trying to get used to using the second method since it seems cleaner to me. (usually)
Glad it finally worked. It could be optimized a bit depending on what you are doing. Returning false might be bad if you are expecting negative numbers... In that case, an abort would be best..
pub get_value(p_input, p_header) | pos pos := str.instr(p_input, p_header) ' look for header in input if (pos => 0) return str.asc2dec(p_input+pos+strsize(p_header), 10) else return -1I realize that you have a single character and colon, but what if your input changes to something like "TEMP=123 FLOW=456"? My method will still work. The lesson here is to write code that solves the immediate problem, yet is open to others within the realm of possibility.
wifi.connect(0,3000,string("44.213.1.12")) 'showing a IP in decimal text with dots is the standard and raw 32bit hex is not common ... PUB connect(sockethandle,port,IPv4) hci_byte(parsedot(IPv4,0)) 'Destination IPv4 (Big-endian) hci_byte(parsedot(IPv4,1)) hci_byte(parsedot(IPv4,2)) hci_byte(parsedot(IPv4,3)) ... PUB parsedot(mystr, dotn) i:=0 repeat dotn 'repeat 0 is like a nop repeat until byte[mystr][i++]== 46 repeat until byte[mystr][i]== 46 or byte[mystr][i]== 0 result *=10 'use system-var result, it's always initialized to zero result += byte[mystr][i++]-48 'get asc to decAnything with "R:C:" was send from the browser. The next line is the data from the Prop.
When I call command 10, the string looks like this : <R:C:10:N:6:V:74:#> I always get "Failed Saving!"
Here are bits and pieces of my receiving / processing code on the Prop :
VAR LONG which_one LONG value PUB Handle_Wifi | char, i xb.Start(XB_OUT, XB_IN, %0000, 9_600) pause(5000) xb.str(string(BEGIN_CHR)) xb.str(string("Solar Connected!")) xb.str(string(END_CHR)) index := 0 i := 0 repeat i++ index := 0 if i => 10 xb.str(string(BEGIN_CHR)) xb.str(string("PING")) xb.str(string(END_CHR)) i := 0 char := xb.rxtime(2000) if char == "R" repeat until char == "#" i++ if i == 5 quit if char > 31 and char < 126 i := 0 buffer[index++] := char char := xb.rxtime(1000) xb.rxflush buffer[index]~ ' ****************** Maybe I am not doing the z-string correctly? **************** i := 0 if(char == "#") AND strsize(@buffer) > 4 Process_Data bytefill(@buffer, 0, 256) else bytefill(@buffer, 0, 256) elseif char > -1 xb.rxflush char := -1 PUB Process_Data command := extract_data(@buffer, string("C:")) pause(100) if(command => 0) case command 1 : '............. 10 : ' PROGRAM VALUE TO VARIABLE which_one := extract_data(@buffer, string("N:")) value := extract_data(@buffer, string("V:")) if which_one > -1 AND value > -1 AND str.is_number(which_one) == true AND str.is_number(value) == true copy_to(which_one, value) save_flag := 1 ' Trigger Save in Main loop xb.str(string(BEGIN_CHR)) xb.str(string("Saved New Value!")) xb.str(string(END_CHR)) else xb.str(string(BEGIN_CHR)) xb.str(string("Failed Saving!")) xb.str(string(END_CHR)) OTHER : move_EW_flag := 0 move_UD_flag := 0 move_to_park := 0 xb.str(string(BEGIN_CHR)) xb.str(string("Unknown Command : ")) xb.dec(command) xb.str(string(END_CHR)) PUB extract_data(p_input, p_header) | pos pos := str.instr(p_input, p_header) ' look for header in input if (pos => 0) return str.asc2dec(p_input+pos+strsize(p_header), 10) else return -1PUB Handle_Wifi | char, i, x xb.Start(XB_OUT, XB_IN, %0000, 9_600) pause(5000) xb.str(string(BEGIN_CHR)) xb.str(string("Solar Connected!")) xb.str(string(END_CHR)) index := 0 i := 0 repeat i++ x := 0 index := 0 if i => 20 xb.str(string(BEGIN_CHR)) xb.str(string("PING")) xb.str(string(END_CHR)) i := 0 char := xb.rxtime(2000) if char == "R" repeat until char == "#" x++ if x => 5 quit if char > 31 and char < 126 x := 0 buffer[index++] := char char := xb.rxtime(100) xb.rxflush buffer[index++]~ i := 0 if(str.instr(@buffer, string("R") => 0 AND str.instr(@buffer, string("#")) => 0)) Process_Data bytefill(@buffer, 0, 255) else bytefill(@buffer, 0, 255) 'elseif char > -1 ' xb.rxflush ' bytefill(@buffer, 0, 255)