B4R Tutorial Strings and Bytes

Status
Not open for further replies.

Erel

Administrator
Staff member
Licensed User
B4R strings are different than in other B4X tools. The reasons for these differences are:
1. Very limited memory.
2. Lack of Unicode encoders.

A String object in B4R is the same as C char* string. It is an array of bytes with an additional zero byte at the end.
The requirement of the last zero byte makes it impossible to create a substring without copying the memory to a new address. For that reason arrays of bytes are preferable over Strings. The various string related methods work with arrays of bytes.

Converting a string to an array of bytes is very simple and doesn't involve any memory copying. The compiler will do it automatically when needed:
B4X:
Dim b() As Byte = "abc" 'equivalent to Dim b() As Byte = "abc".GetBytes
Concatenation

The & operator is not available in B4R. If you do need to concatenate strings or arrays of bytes then you can use JoinStrings or JoinBytes keywords. They are more efficient as they concatenate all the elements at once.

In most cases you don't need to concatenate strings. A better solution for example when sending strings (or bytes) with AsyncStreams:
B4X:
AStream.Write("The current value is: ").Write(s).Write(" and the other value is: ")
AStream.Write(NumberFormat(d, 0,0))
String Methods

The standard string methods are available in ByteConverter type (rRandomAccessFile library).

They are similar to the string methods in other B4X tools:
B4X:
Private Sub AppStart
   Serial1.Initialize(115200)
   Log("AppStart")  
   Dim bc As ByteConverter
   Log("IndexOf: ", bc.IndexOf("0123456", "3")) 'IndexOf: 3
   Dim b() As Byte = " abc,def,ghijkl "
   Log("Substring: ", bc.SubString(b, 3)) 'Substring: c,def,ghijkl
   Log("Trim: ", bc.Trim(b)) 'Trim: abc,def,ghijkl
   For Each s() As Byte In bc.Split(b, ",")
     Log("Split: ", s)
     'Split: abc
     'Split: def
     'Split: ghijkl
   Next
   Dim c As String = JoinStrings(Array As String("Number of millis: ", Millis, CRLF, "Number of micros: ", Micros))
   Log("c = ", c)
   Dim b() As Byte = bc.SubString2(c, 0, 5)
   b(0) = Asc("X")
   Log("b = ", b)
   Log("c = ", c) 'first character will be X
End Sub
Note how both strings and array of bytes can be used as the compiler converts strings to arrays of bytes automatically.

With the exception of JoinStrings, none of the above methods make a copy of the original string / bytes.
This means that modifying the returned array as in the last three lines will also modify the original array.

It will also happen with string literals that all share the same memory block:
B4X:
Private Sub AppStart
   Serial1.Initialize(115200)
   Log("AppStart")  
   Dim bc As ByteConverter
   Dim b() As Byte = bc.Trim("abcdef ")
   b(0) = Asc("M") 'this line will change the value of the literal string
   dim s as String = "abcdef "
   Log(s) 'Mbcdef
End Sub
Encoding

There is no real support for Unicode encodings in Arduino. You are always working with raw bytes.

B4X:
Dim s As String = "אראל"
Log(s) 'will print correctly
Log(s.Length) '8 because each of the non-ASCII characters in this case takes two bytes
 
Last edited:

Erel

Administrator
Staff member
Licensed User
Quiz for the Arduino experts (who also like to read the generated C code).

B4X:
Private Sub AppStart
  Serial1.Initialize(115200)
  Log("AppStart")  
  Dim bc As ByteConverter
  Dim b() As Byte = bc.Trim("abcdef ")
  b(0) = Asc("M") 'this line will change the value of the literal string
  Dim s As String = "abcdef "
  Log(s) 'Mbcdef
  Log("abcdef ") '???
End Sub
What will be printed from the last line and why?
 
Last edited:

Cableguy

Expert
Licensed User
I would say it prints exactly "abcdef " because the string is not allocated into memory(?)
I'm no Arduino expert, nor B4x for that matter, but the previous log(s) is coming from an allocated variable who had its original value previously changed... if that makes any sense
 

Erel

Administrator
Staff member
Licensed User
ByteConverter currently does not include a Replace string method. You can use this code instead:

B4X:
Private Sub ReplaceString(Original() As Byte, SearchFor() As Byte, ReplaceWith() As Byte) As Byte()
   'count number of occurrences
   Dim bc2 As ByteConverter
   Dim c As Int = 0
   Dim i As Int
   If SearchFor.Length <> ReplaceWith.Length Then
     i = bc2.IndexOf(Original, SearchFor)
     Do While i > -1
       c = c + 1
       i = bc2.IndexOf2(Original, SearchFor, i + SearchFor.Length)
     Loop
   End If
   Dim result(Original.Length + c * (ReplaceWith.Length - SearchFor.Length)) As Byte
   Dim prevIndex As Int = 0
   Dim targetIndex As Int = 0
   i = bc2.IndexOf(Original, SearchFor)
   Do While i > -1
     bc2.ArrayCopy2(Original, prevIndex, result, targetIndex, i - prevIndex)
     targetIndex = targetIndex + i - prevIndex
     bc2.ArrayCopy2(ReplaceWith, 0, result, targetIndex, ReplaceWith.Length)
     targetIndex = targetIndex + ReplaceWith.Length
     prevIndex = i + SearchFor.Length
     i = bc2.IndexOf2(Original, SearchFor, prevIndex)
   Loop
   If prevIndex < Original.Length Then
     bc2.ArrayCopy2(Original, prevIndex, result, targetIndex, Original.Length - prevIndex)
   End If
   Return result
End Sub
Example:
B4X:
Dim b() As Byte = ReplaceString("a  b   c   d ef ", " ","")
Log(b) 'abcdef
Log(ReplaceString("ab%20cd%20e", "%20", " ")) 'ab cd e
 

Santiago Barreiro

Member
Licensed User
Hello...

Kind of helps, but i have the following need.

Lets say data is:

1234sep5678sep9876

And i need to get the 5678, that is the second field
I am working also in XOJO, that has the nthField(data,separator,index)
in Xojo I will simple do:

XOJO sample:
dim iResult as string
iResult=nthField("1234sep5678sep9876","sep",2)
How would one do this in B4A?

Regards
 

William Lancee

Active Member
Licensed User
B4X:
'For B4R Arduino
'Depends on rRandomAccessFile library

Sub nthField(s As String, sep As String, n As Int) As Byte()
    Dim bc As ByteConverter
    Dim k, lastk, slength As Int = -1
    slength = sep.length
    For j = 0 To n-1
        k = bc.IndexOf2(s, sep, lastk)
        Log(lastk,TAB,k)
        If k = -1 Or j = n-1 Then Exit
        lastk = k + slength
    Next
    If k = -1 Then
        Return "n/a"
    Else
        Return bc.SubString2(s, lastk, k)
    End If
End Sub
 
Last edited by a moderator:

Santiago Barreiro

Member
Licensed User
Just the same but you need to add this Sub.
(Just remember that B4X is zero-based!)

B4X:
Sub nthField (s As String, sep As String, index As Int) As String
    Dim v() As String = Regex.Split(sep, s)
    Return v(index-1)
End Sub
Or even shorter...
B4X:
Sub nthField (s As String, sep As String, index As Int) As String
    Return Regex.Split(sep, s)(index-1)
End Sub
Excellent... Thanks
 
Status
Not open for further replies.
Top