Is this a bug or expected behaviour?

Here’s some very rough (barely tested) code that implements grapheme cluster friendly string manipulation methods as extensions to the string data type.

Please be aware that they will be slower than normal string functions due to the use of the Characters iterator. I believe they could be made faster if Xojo implemented them directly within the framework using the OS / ICU string C functions but even those require some kind of character iteration so would never be as fast as the current Xojo string functions.

Public Function CharacterLength(Extends pString As String) As Integer
  #Pragma DisableBackgroundTasks
  #Pragma DisableBoundsChecking
  #Pragma StackOverflowChecking False
  
  Dim theResult As Integer
  
  theResult = 0
  
  For Each char As String In pString.Characters
    theResult = theResult + 1
  Next
  
  Return theResult
End Function
Public Function CharacterLeft(Extends pString As String, pLength As Integer) As String
  #Pragma DisableBackgroundTasks
  #Pragma DisableBoundsChecking
  #Pragma StackOverflowChecking False
  
  Dim theResult(-1) As String
  
  If pLength > 0 Then
    For Each char As String In pString.Characters
      theResult.Append(char)
      
      pLength = pLength - 1
      
      If pLength = 0 Then
        Exit For
      End If
    Next
  End If
  
  Return Join(theResult, "")
End Function
Public Function CharacterSplit(Extends pString As String) As String()
  #Pragma DisableBackgroundTasks
  #Pragma DisableBoundsChecking
  #Pragma StackOverflowChecking False
  
  Dim theResult(-1) As String
  
  For Each char As String In pString.Characters
    theResult.Append(char)
  Next
  
  Return theResult
End Function
Public Function CharacterRight(Extends pString As String, pLength As Integer) As String
  #Pragma DisableBackgroundTasks
  #Pragma DisableBoundsChecking
  #Pragma StackOverflowChecking False
  
  Dim theResult(-1) As String
  Dim charArray(-1) As String
  Dim count, i As Int32
  
  If pLength > 0 Then
    charArray = pString.CharacterSplit
    
    count = UBound(charArray)
    
    i = Max(count - (pLength - 1), 0)
    
    While i <= count
      theResult.Append(charArray(i))
      
      i = i + 1
    Wend
  End If
  
  Return Join(theResult, "")
End Function
Public Function CharacterMiddle(Extends pString As String, pStart As Integer, Optional pLength As Integer = -1) As String
  #Pragma DisableBackgroundTasks
  #Pragma DisableBoundsChecking
  #Pragma StackOverflowChecking False
  
  Dim theResult(-1) As String
  Dim charArray(-1) As String
  Dim count, i As Int32
  
  If (pLength = -1) Or (pLength > 0) Then
    charArray = pString.CharacterSplit
    
    i = pStart
    
    If pLength = -1 Then
      count = UBound(charArray)
    Else
      count = Min(i + (pLength - 1), UBound(charArray))
    End If
    
    While i <= count
      theResult.Append(charArray(i))
      
      i = i + 1
    Wend
  End If
  
  Return Join(theResult, "")
End Function
Public Function CharacterIndexOf(Extends pString As String, Optional pStart As Integer = 1, Optional pFind As String, Optional pOptions As ComparisonOptions = ComparisonOptions.CaseInsensitive, Optional pLocale As Locale) As Integer
  #Pragma DisableBackgroundTasks
  #Pragma DisableBoundsChecking
  #Pragma StackOverflowChecking False
  
  Dim theResult As Integer
  Dim charIndex As Integer
  Dim findCharArray(-1) As String
  Dim findCharArrayUBound As Integer
  Dim findCharArrayIndex As Integer
  
  theResult = -1
  
  If Len(pFind) > 0 Then
    findCharArray = pFind.CharacterSplit
    findCharArrayUBound = UBound(findCharArray)
    findCharArrayIndex = 0
    
    charIndex = 0
    For Each char As String In pString.Characters
      If charIndex >= pStart Then
        If char.Compare(findCharArray(findCharArrayIndex), pOptions, pLocale) = 0 Then
          If findCharArrayIndex = 0 Then
            theResult = charIndex
          End If
          
          findCharArrayIndex = findCharArrayIndex + 1
          
          If findCharArrayIndex > findCharArrayUBound Then
            Exit For
          End If
        Else
          theResult = -1
          findCharArrayIndex = 0
        End If
      End If
      
      charIndex = charIndex + 1
    Next
  Else
    theResult = pStart
  End If
  
  Return theResult
End Function
Public Function CharacterIndexOf(Extends pString As String, pFind As String, Optional pOptions As ComparisonOptions = ComparisonOptions.CaseInsensitive, Optional pLocale As Locale) As Integer
  Return pString.CharacterIndexOf(-1, pFind, pOptions, pLocale)
End Function
4 Likes