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