Split strings: For arrays, lists, with multi separators

Share your advanced PureBasic knowledge/code with the community.
technicorn
Enthusiast
Enthusiast
Posts: 105
Joined: Wed Jan 18, 2006 7:40 pm
Location: Hamburg

Split strings: For arrays, lists, with multi separators

Post by technicorn »

Sorry for the delay, wasn't as easy as I thought, but here it is.

I hope the comments on every part are enough for a start.
I'm working on the joining part right now.

The procedures should be cross platform, although I can only test it under Windows,
and they are for ASCII and unicode compilation.


You always have to include the first part, because it defines macros and constants:

Code: Select all

CompilerSelect SizeOf(Character)
  CompilerCase 1
  CompilerCase 2
  CompilerCase 4
  CompilerDefault: CompilerError "SizeOf(Character) is not 1, 2 or 4"
CompilerEndSelect

Macro UBound(x):(PeekL((@x) - 8) - 1):EndMacro

CompilerIf #PB_Compiler_Unicode
  Macro SplitJoinStringType:p-unicode:EndMacro
CompilerElse
  Macro SplitJoinStringType:p-ascii:EndMacro
CompilerEndIf

#SplitString_NoEmptyParts         = 1 << 16
#SplitString_MultiSingleCharacter = 1 << 17
#SplitString_NoReDim              = 1 << 18

#JoinString_NoEmptyParts          = 1 << 16
#JoinString_ForceEndSeparator     = 1 << 17
#JoinString_KeepOldString         = 1 << 18
This part is for splitting into arrays:

Code: Select all

; Splitting a string into parts
;
; strIn     = String to split for SplitStringToArray()
;             Pointer to memory to split for SplitMemoryToArray()
;             The memory-block must have a terminating 0 character!
;
; strOut()  = String-array which will contain the parts,
;             will be automaticly redimed to fit all parts.
;
; separator = Single- or multi-character string by which the string will be splitted
;
; splitFlags= #SplitString_NoEmptyParts         : Will omit empty parts of strIn
;             #SplitString_MultiSingleCharacter : If separator-string is more than one character,
;                                                 every single character counts as separator.
;                                                 If not set, the whole separator-string counts
;                                                 as one separator.
;             #SplitString_NoReDim              : strOut() will not be redimed to minimum size
;                                                 at end of procedure.
;             You can use several flags by oring them together with '|'.
;             A value between 1 - 31 can be add to the flags,
;             this will set the minimum step for expanding strOut() in 1024's steps.
;             The standard step is 256, which will be enough for any normal text-string,
;             but if strIn contains many parts, or you split large memory-blocks,
;             it might give a speedup if you set a higher step value.
;             
;
; splitTo   = Optional parameter. Will set the start index for strOut()
;             the parts will be splitted to, so you can split
;             several strings into one array
;
; *ubValue  = Optional pointer to long, will be set to maximum index of strOut() on return
;
; Returns the number of parts for actual splitting
;
; If the separator is 0, or the strIn contains no separators,
; strOut() will containe the whole string and 1 is returned
;
; If strIn is empty or a 0 pointer, strOut() will not be changed, and 0 is returned
Procedure.l _SplitToArray(*strIn.Character, strOut.s(1), *separator.Character, splitFlags.l = 0, splitTo.l = 0, *ubValue.Long = 0)
  Protected strOutUBound.l, uBoundStep.l, splitToIdx.l
  Protected firstSeparatorCh.l, SecondSeparatorCh.l
  Protected separatorCh.l, strInCh.l
  Protected *pStrIn.Character, *thirdSeparatorCh.Character
  Protected *splitStart.Character, *splitEnd.Character

  strOutUBound = UBound(strOut()) ; Get size of array
  splitToIdx = splitTo
  If splitFlags & 31
    uBoundStep = 1024 * (splitFlags & 31)
  Else
    uBoundStep = 256
  EndIf

  If *strIn
    Repeat
      strInCh = *strIn\c
      If *separator
        firstSeparatorCh = *separator\c
        *separator + SizeOf(Character)
      EndIf
  
      If Not firstSeparatorCh
        If strInCh
          If strOutUBound < splitToIdx
            strOutUBound = splitToIdx
            ReDim strOut.s(strOutUBound)
          EndIf
          strOut(splitToIdx) = PeekS(*strIn)
          splitToIdx + 1
          Break
        EndIf
        Break
      EndIf
  
      *pStrIn = *strIn
      *splitStart = *strIn
      secondSeparatorCh = *separator\c
      If secondSeparatorCh  ; *** Split by multicharacter-separator ***
        If splitFlags & #SplitString_MultiSingleCharacter ; *** Split by mutlicharacter-separator single characters ***
          *separator + SizeOf(Character)
          *thirdSeparatorCh = *separator
          If splitFlags & #SplitString_NoEmptyParts
            While strInCh
              Repeat
                Repeat
                  If strInCh = firstSeparatorCh Or strInCh = SecondSeparatorCh
                    Continue  ; *** Match ***
                  EndIf
                  *separator = *thirdSeparatorCh
                  While *separator\c
                    If strInCh = *separator\c
                      Break 2 ; *** Match ***
                    EndIf
                    *separator + SizeOf(Character)
                  Wend
                  Break 2
                Until 1
                If *pStrIn <> *splitStart
                  If splitToIdx > strOutUBound
                    strOutUBound + uBoundStep
                    uBoundStep << 1
                    ReDim strOut.s(strOutUBound)
                  EndIf
                  CompilerSelect SizeOf(Character)
                    CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
                    CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                    CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
                  CompilerEndSelect
                  splitToIdx + 1
                EndIf
                *splitStart = *pStrIn + SizeOf(Character)
              Until 1
              *pStrIn + SizeOf(Character)
              strInCh = *pStrIn\c
            Wend

            If *pStrIn <> *splitStart
              If splitToIdx > strOutUBound
                strOutUBound + uBoundStep
                uBoundStep << 1
                ReDim strOut.s(strOutUBound)
              EndIf
              CompilerSelect SizeOf(Character)
                CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
                CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
              CompilerEndSelect
              splitToIdx + 1
            EndIf
          Else  ; *** Keep empty parts ***
            While strInCh
              Repeat
                Repeat
                  If strInCh = firstSeparatorCh Or strInCh = SecondSeparatorCh
                    Continue  ; *** Match ***
                  EndIf
                  *separator = *thirdSeparatorCh
                  While *separator\c
                    If strInCh = *separator\c
                      Break 2 ; *** Match ***
                    EndIf
                    *separator + SizeOf(Character)
                  Wend
                  Break 2
                Until 1
                If splitToIdx > strOutUBound
                  strOutUBound + uBoundStep
                  uBoundStep << 1
                  ReDim strOut.s(strOutUBound)
                EndIf
                CompilerSelect SizeOf(Character)
                  CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
                  CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                  CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
                CompilerEndSelect
                splitToIdx + 1
                *splitStart = *pStrIn + SizeOf(Character)
              Until 1
              *pStrIn + SizeOf(Character)
              strInCh = *pStrIn\c
            Wend

            If splitToIdx > strOutUBound
              strOutUBound + uBoundStep
              uBoundStep << 1
              ReDim strOut.s(strOutUBound)
            EndIf
            CompilerSelect SizeOf(Character)
              CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
              CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
              CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
            CompilerEndSelect
            splitToIdx + 1
          EndIf
        Else ; *** Split by multicharacter-separator full string ***
          *separator + SizeOf(Character)
          *thirdSeparatorCh = *separator
          If splitFlags & #SplitString_NoEmptyParts
            While strInCh
              Repeat
                If strInCh = firstSeparatorCh
                  *splitEnd = *pStrIn
                  *pStrIn + SizeOf(Character)
                  If *pStrIn\c <> secondSeparatorCh
                    strInCh = *pStrIn\c
                    Continue
                  EndIf
                  *pStrIn + SizeOf(Character)
                  *separator = *thirdSeparatorCh
                  separatorCh = *separator\c
                  strInCh = *pStrIn\c
                  Repeat
                    While separatorCh
                      If strInCh <> separatorCh
                        *pStrIn = *splitEnd
                        Break 2
                      EndIf
                      *pStrIn + SizeOf(Character)
                      strInCh = *pStrIn\c
                      *separator + SizeOf(Character)
                      separatorCh = *separator\c
                    Wend
                    If *splitStart <> *splitEnd
                      If splitToIdx > strOutUBound
                        strOutUBound + uBoundStep
                        uBoundStep << 1
                        ReDim strOut.s(strOutUBound)
                      EndIf
                      CompilerSelect SizeOf(Character)
                        CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *splitEnd - *splitStart)
                        CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*splitEnd - *splitStart) >> 1)
                        CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*splitEnd - *splitStart) >> 2)
                      CompilerEndSelect
                      splitToIdx + 1
                    EndIf
                    *splitStart = *pStrIn
                    Break 2
                  Until 1
                EndIf
                *pStrIn + SizeOf(Character)
                strInCh = *pStrIn\c
              Until 1
            Wend
        
            If *splitStart <> *pStrIn
              If splitToIdx > strOutUBound
                strOutUBound + uBoundStep
                uBoundStep << 1
                ReDim strOut.s(strOutUBound)
              EndIf
              CompilerSelect SizeOf(Character)
                CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
                CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
              CompilerEndSelect
              splitToIdx + 1
            EndIf
          Else  ; *** Keep empty parts ***
            While strInCh
              Repeat
                If strInCh = firstSeparatorCh
                  *splitEnd = *pStrIn
                  *pStrIn + SizeOf(Character)
                  If *pStrIn\c <> secondSeparatorCh
                    strInCh = *pStrIn\c
                    Continue
                  EndIf
                  *pStrIn + SizeOf(Character)
                  *separator = *thirdSeparatorCh
                  separatorCh = *separator\c
                  strInCh = *pStrIn\c
                  Repeat
                    While separatorCh
                      If strInCh <> separatorCh
                        *pStrIn = *splitEnd
                        Break 2
                      EndIf
                      *pStrIn + SizeOf(Character)
                      strInCh = *pStrIn\c
                      *separator + SizeOf(Character)
                      separatorCh = *separator\c
                    Wend
                    If splitToIdx > strOutUBound
                      strOutUBound + uBoundStep
                      uBoundStep << 1
                      ReDim strOut.s(strOutUBound)
                    EndIf
                    CompilerSelect SizeOf(Character)
                      CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *splitEnd - *splitStart)
                      CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*splitEnd - *splitStart) >> 1)
                      CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*splitEnd - *splitStart) >> 2)
                    CompilerEndSelect
                    splitToIdx + 1
                    *splitStart = *pStrIn
                    Break 2
                  Until 1
                EndIf
                *pStrIn + SizeOf(Character)
                strInCh = *pStrIn\c
              Until 1
            Wend
    
            If splitToIdx > strOutUBound
              strOutUBound + uBoundStep
              uBoundStep << 1
              ReDim strOut.s(strOutUBound)
            EndIf
            CompilerSelect SizeOf(Character)
              CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
              CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
              CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
            CompilerEndSelect
            splitToIdx + 1
          EndIf
        EndIf
      Else  ; *** Split by singlecharacter-separator ***
        If splitFlags & #SplitString_NoEmptyParts
          While strInCh
            If strInCh = firstSeparatorCh
              If *pStrIn <> *splitStart
                If splitToIdx > strOutUBound
                  strOutUBound + uBoundStep
                  uBoundStep << 1
                  ReDim strOut.s(strOutUBound)
                EndIf
                CompilerSelect SizeOf(Character)
                  CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
                  CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                  CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
                CompilerEndSelect
                splitToIdx + 1
              EndIf
              *splitStart = *pStrIn + SizeOf(Character)
            EndIf
            *pStrIn + SizeOf(Character)
            strInCh = *pStrIn\c
          Wend
  
          If *pStrIn <> *splitStart
            If splitToIdx > strOutUBound
              strOutUBound + uBoundStep
              uBoundStep << 1
              ReDim strOut.s(strOutUBound)
            EndIf
            CompilerSelect SizeOf(Character)
              CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
              CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
              CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
            CompilerEndSelect
            splitToIdx + 1
          EndIf
        Else  ; *** Keep empty parts ***
          While strInCh
            If strInCh = firstSeparatorCh
              If splitToIdx > strOutUBound
                strOutUBound + uBoundStep
                uBoundStep << 1
                ReDim strOut.s(strOutUBound)
              EndIf
              CompilerSelect SizeOf(Character)
                CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
                CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
              CompilerEndSelect
              splitToIdx + 1
              *splitStart = *pStrIn + SizeOf(Character)
            EndIf
            *pStrIn + SizeOf(Character)
            strInCh = *pStrIn\c
          Wend
  
          If splitToIdx > strOutUBound
            strOutUBound + uBoundStep
            uBoundStep << 1
            ReDim strOut.s(strOutUBound)
          EndIf
          CompilerSelect SizeOf(Character)
            CompilerCase 1  : strOut(splitToIdx) = PeekS(*splitStart, *pStrIn - *splitStart)
            CompilerCase 2  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
            CompilerCase 4  : strOut(splitToIdx) = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
          CompilerEndSelect
          splitToIdx + 1
        EndIf
      EndIf

      If Not (splitFlags & #SplitString_NoReDim)
        ReDim strOut.s(splitToIdx - 1)
      EndIf
    Until 1
  EndIf
  
  If *ubValue: *ubValue\l = strOutUBound: EndIf
  ProcedureReturn splitToIdx - splitTo
EndProcedure
Prototype.l pt_SplitStringToArray(strIn.SplitJoinStringType, strOut.s(1), separator.SplitJoinStringType, splitFlags.l = 0, splitTo.l = 0, *ubValue.Long = 0)
Prototype.l pt_SplitMemoryToArray(*strIn.Character, strOut.s(1), separator.SplitJoinStringType, splitFlags.l = 0, splitTo.l = 0, *ubValue.Long = 0)
Global SplitStringToArray.pt_SplitStringToArray = @_SplitToArray()
Global SplitMemoryToArray.pt_SplitMemoryToArray = @_SplitToArray()
And this part for lists:

Code: Select all

; Splitting a string into parts
; Splitting a string into parts
;
; strIn     = String to split for SplitStringToArray()
;             Pointer to memory to split for SplitMemoryToArray()
;             The memory-block must have a terminating 0 character!
;
; strOut()  = String-list which will contain the parts.
;             You have to set the list position before calling this procedure,
;             or you can use ClearList() if you only want the parts from actual splitting
;             Either way, all new parts will be add after the actual list position.
;
; separator = Single- or multi-character string by which the string will be splitted
;
; splitFlags= #SplitString_NoEmptyParts         : Will omit empty parts of strIn
;             #SplitString_MultiSingleCharacter : If separator-string is more than one character,
;                                                 every single character counts as separator.
;                                                 If not set, the whole separator-string counts
;                                                 as one separator.
;             #SplitString_NoReDim              : Does nothing for lists, also the expand step
;                                                 value will be ignored.
;
;             You can use several flags by oring them together with '|'.
;
; Returns the number of parts for actual splitting
;
; If the separator is 0, or the strIn contains no separators,
; strOut() will containe the whole string and 1 is returned
;
; If strIn is empty or a 0 pointer, strOut() will not be changed, and 0 is returned
Procedure.l _SplitToList(*strIn.Character, strOut.s(), *separator.Character, splitFlags.l = 0)
  Protected splitCnt.l
  Protected firstSeparatorCh.l, SecondSeparatorCh.l
  Protected separatorCh.l, strInCh.l
  Protected *pStrIn.Character, *thirdSeparatorCh.Character
  Protected *splitStart.Character, *splitEnd.Character

  If *strIn
    Repeat
      strInCh = *strIn\c
      If *separator
        firstSeparatorCh = *separator\c
        *separator + SizeOf(Character)
      EndIf
  
      If Not firstSeparatorCh
        If strInCh
          AddElement(strOut())
          strOut() = PeekS(*strIn)
          splitCnt + 1
          Break
        EndIf
        Break
      EndIf
  
      *pStrIn = *strIn
      *splitStart = *strIn
      secondSeparatorCh = *separator\c
      If secondSeparatorCh  ; *** Split by multicharacter-separator ***
        If splitFlags & #SplitString_MultiSingleCharacter ; *** Split by mutlicharacter-separator single characters ***
          *separator + SizeOf(Character)
          *thirdSeparatorCh = *separator
          If splitFlags & #SplitString_NoEmptyParts
            While strInCh
              Repeat
                Repeat
                  If strInCh = firstSeparatorCh Or strInCh = SecondSeparatorCh
                    Continue  ; *** Match ***
                  EndIf
                  *separator = *thirdSeparatorCh
                  While *separator\c
                    If strInCh = *separator\c
                      Break 2 ; *** Match ***
                    EndIf
                    *separator + SizeOf(Character)
                  Wend
                  Break 2
                Until 1
                If *pStrIn <> *splitStart
                  AddElement(strOut())
                  CompilerSelect SizeOf(Character)
                    CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
                    CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                    CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
                  CompilerEndSelect
                  splitCnt + 1
                EndIf
                *splitStart = *pStrIn + SizeOf(Character)
              Until 1
              *pStrIn + SizeOf(Character)
              strInCh = *pStrIn\c
            Wend

            If *pStrIn <> *splitStart
              AddElement(strOut())
              CompilerSelect SizeOf(Character)
                CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
                CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
              CompilerEndSelect
              splitCnt + 1
            EndIf
          Else  ; *** Keep empty parts ***
            While strInCh
              Repeat
                Repeat
                  If strInCh = firstSeparatorCh Or strInCh = SecondSeparatorCh
                    Continue  ; *** Match ***
                  EndIf
                  *separator = *thirdSeparatorCh
                  While *separator\c
                    If strInCh = *separator\c
                      Break 2 ; *** Match ***
                    EndIf
                    *separator + SizeOf(Character)
                  Wend
                  Break 2
                Until 1
                AddElement(strOut())
                CompilerSelect SizeOf(Character)
                  CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
                  CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                  CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
                CompilerEndSelect
                splitCnt + 1
                *splitStart = *pStrIn + SizeOf(Character)
              Until 1
              *pStrIn + SizeOf(Character)
              strInCh = *pStrIn\c
            Wend

            AddElement(strOut())
            CompilerSelect SizeOf(Character)
              CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
              CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
              CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
            CompilerEndSelect
            splitCnt + 1
          EndIf
        Else ; *** Split by multicharacter-separator full string ***
          *separator + SizeOf(Character)
          *thirdSeparatorCh = *separator
          If splitFlags & #SplitString_NoEmptyParts
            While strInCh
              Repeat
                If strInCh = firstSeparatorCh
                  *splitEnd = *pStrIn
                  *pStrIn + SizeOf(Character)
                  If *pStrIn\c <> secondSeparatorCh
                    strInCh = *pStrIn\c
                    Continue
                  EndIf
                  *pStrIn + SizeOf(Character)
                  *separator = *thirdSeparatorCh
                  separatorCh = *separator\c
                  strInCh = *pStrIn\c
                  Repeat
                    While separatorCh
                      If strInCh <> separatorCh
                        *pStrIn = *splitEnd
                        Break 2
                      EndIf
                      *pStrIn + SizeOf(Character)
                      strInCh = *pStrIn\c
                      *separator + SizeOf(Character)
                      separatorCh = *separator\c
                    Wend
                    If *splitStart <> *splitEnd
                      AddElement(strOut())
                      CompilerSelect SizeOf(Character)
                        CompilerCase 1  : strOut() = PeekS(*splitStart, *splitEnd - *splitStart)
                        CompilerCase 2  : strOut() = PeekS(*splitStart, (*splitEnd - *splitStart) >> 1)
                        CompilerCase 4  : strOut() = PeekS(*splitStart, (*splitEnd - *splitStart) >> 2)
                      CompilerEndSelect
                      splitCnt + 1
                    EndIf
                    *splitStart = *pStrIn
                    Break 2
                  Until 1
                EndIf
                *pStrIn + SizeOf(Character)
                strInCh = *pStrIn\c
              Until 1
            Wend
        
            If *splitStart <> *pStrIn
              AddElement(strOut())
              CompilerSelect SizeOf(Character)
                CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
                CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
              CompilerEndSelect
              splitCnt + 1
            EndIf
          Else  ; *** Keep empty parts ***
            While strInCh
              Repeat
                If strInCh = firstSeparatorCh
                  *splitEnd = *pStrIn
                  *pStrIn + SizeOf(Character)
                  If *pStrIn\c <> secondSeparatorCh
                    strInCh = *pStrIn\c
                    Continue
                  EndIf
                  *pStrIn + SizeOf(Character)
                  *separator = *thirdSeparatorCh
                  separatorCh = *separator\c
                  strInCh = *pStrIn\c
                  Repeat
                    While separatorCh
                      If strInCh <> separatorCh
                        *pStrIn = *splitEnd
                        Break 2
                      EndIf
                      *pStrIn + SizeOf(Character)
                      strInCh = *pStrIn\c
                      *separator + SizeOf(Character)
                      separatorCh = *separator\c
                    Wend
                    AddElement(strOut())
                    CompilerSelect SizeOf(Character)
                      CompilerCase 1  : strOut() = PeekS(*splitStart, *splitEnd - *splitStart)
                      CompilerCase 2  : strOut() = PeekS(*splitStart, (*splitEnd - *splitStart) >> 1)
                      CompilerCase 4  : strOut() = PeekS(*splitStart, (*splitEnd - *splitStart) >> 2)
                    CompilerEndSelect
                    splitCnt + 1
                    *splitStart = *pStrIn
                    Break 2
                  Until 1
                EndIf
                *pStrIn + SizeOf(Character)
                strInCh = *pStrIn\c
              Until 1
            Wend
    
            AddElement(strOut())
            CompilerSelect SizeOf(Character)
              CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
              CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
              CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
            CompilerEndSelect
            splitCnt + 1
          EndIf
        EndIf
      Else  ; *** Split by singlecharacter-separator ***
        If splitFlags & #SplitString_NoEmptyParts
          While strInCh
            If strInCh = firstSeparatorCh
              If *pStrIn <> *splitStart
                AddElement(strOut())
                CompilerSelect SizeOf(Character)
                  CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
                  CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                  CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
                CompilerEndSelect
                splitCnt + 1
              EndIf
              *splitStart = *pStrIn + SizeOf(Character)
            EndIf
            *pStrIn + SizeOf(Character)
            strInCh = *pStrIn\c
          Wend
  
          If *pStrIn <> *splitStart
            AddElement(strOut())
            CompilerSelect SizeOf(Character)
              CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
              CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
              CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
            CompilerEndSelect
            splitCnt + 1
          EndIf
        Else  ; *** Keep empty parts ***
          While strInCh
            If strInCh = firstSeparatorCh
              AddElement(strOut())
              CompilerSelect SizeOf(Character)
                CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
                CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
                CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
              CompilerEndSelect
              splitCnt + 1
              *splitStart = *pStrIn + SizeOf(Character)
            EndIf
            *pStrIn + SizeOf(Character)
            strInCh = *pStrIn\c
          Wend
  
          AddElement(strOut())
          CompilerSelect SizeOf(Character)
            CompilerCase 1  : strOut() = PeekS(*splitStart, *pStrIn - *splitStart)
            CompilerCase 2  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 1)
            CompilerCase 4  : strOut() = PeekS(*splitStart, (*pStrIn - *splitStart) >> 2)
          CompilerEndSelect
          splitCnt + 1
        EndIf
      EndIf
    Until 1
  EndIf
  
  ProcedureReturn splitCnt
EndProcedure
Prototype.l pt_SplitStringToList(strIn.SplitJoinStringType, strOut.s(), separator.SplitJoinStringType, splitFlags.l = 0)
Prototype.l pt_SplitMemoryToList(*strIn.Character, strOut.s(), separator.SplitJoinStringType, splitFlags.l = 0)
Global SplitStringToList.pt_SplitStringToList = @_SplitToList()
Global SplitMemoryToList.pt_SplitMemoryToList = @_SplitToList()