B4R Tutorial Strings and Bytes

Discussion in 'B4R Tutorials' started by Erel, May 10, 2016.

  1. Erel

    Erel Administrator Staff Member Licensed User

    (This tutorial covers features added in B4R v1.00 beta #10.)

    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:
    Code:
    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:
    Code:
    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:
    Code:
    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, 05)
       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:
    Code:
    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.

    Code:
    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
     
  2. Erel

    Erel Administrator Staff Member Licensed User

    Quiz for the Arduino experts (who also like to read the generated C code).

    Code:
    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: May 10, 2016
  3. Cableguy

    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
     
    Peter Simpson likes this.
  4. Erel

    Erel Administrator Staff Member Licensed User

    This is the correct answer. However the explanation is not 100% correct.

    It is still open...
     
    Peter Simpson likes this.
  5. Cableguy

    Cableguy Expert Licensed User

    so I got about 75% right?
     
    Erel likes this.
  6. Erel

    Erel Administrator Staff Member Licensed User

    ByteConverter currently does not include a Replace string method. You can use this code instead:

    Code:
    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:
    Code:
    Dim b() As Byte = ReplaceString("a  b   c   d ef "" ","")
    Log(b) 'abcdef
    Log(ReplaceString("ab%20cd%20e""%20"" ")) 'ab cd e
     
    Peter Simpson likes this.
Loading...