xsharp.eu • VFP *TRIM() functions
Page 1 of 1

VFP *TRIM() functions

Posted: Wed May 20, 2020 7:20 pm
by atlopes
As an experiment on varying number of parameters in a function declaration, a proposal of the implementation of *Trim() functions:

  • ALLTRIM(Expression [, nFlags] [, cParseChar [, cParseChar2 [, ...]]])
  • LTRIM(Expression [, nFlags] [, cParseChar [, cParseChar2 [, ...]]])
  • RTRIM(cExpression [, nFlags] [, cParseChar [, cParseChar2 [, ...]]])
TRIM() = RTRIM(), so a header file could be used to add it to the list.

Testing (but no stress test):

Code: Select all

Procedure Trim_Test ()
    
    ? 'Alltrim(" ") == ""', IIF(Alltrim(" ") == "", "OK", "Fail")
    ? 'Alltrim(e" n ", 0, " ", CHR(9), CHR(10), CHR(13)) == ""', IIF(Alltrim(e" n ", 0, " ", CHR(9), CHR(10), CHR(13)) == "", "OK", "Fail")
    ? 'Alltrim("Test ") == "Test"', IIF(Alltrim("Test ") == "Test", "OK", "Fail")
    ? 'Alltrim(e"Test n ", 0, " ", CHR(9), CHR(10), CHR(13)) == "Test"', IIF(Alltrim(e"Test n ", 0, " ", CHR(9), CHR(10), CHR(13)) == "Test", "OK", "Fail")
    ? 'Alltrim("TeTeTest", 0, "te") == "TeTeTest"', IIF(Alltrim("TeTeTest", 0, "te") == "TeTeTest", "OK", "Fail")
    ? 'Alltrim("TeTeTest", 1, "te") == "st"', IIF(Alltrim("TeTeTest", 1, "te") == "st", "OK", "Fail")
    ? 'Alltrim("TeTeTest", 0, "t", "e") == "TeTeTes"', IIF(Alltrim("TeTeTest", 0, "t", "e") == "TeTeTes", "OK", "Fail")
    ? 'Alltrim("TeTeTest", 1, "t", "e") == "s"', IIF(Alltrim("TeTeTest", 1, "t", "e") == "s", "OK", "Fail")
    ? 'Alltrim("***abc*", 0, "*") == "abc"', IIF(Alltrim("***abc*", 0, "*") == "abc", "OK", "Fail")
    ? 'Ltrim("***abc*", 0, "*") == "abc*"', IIF(Ltrim("***abc*", 0, "*") == "abc*", "OK", "Fail")
    ? 'Rtrim("***abc*", 0, "*") == "***abc"', IIF(Rtrim("***abc*", 0, "*") == "***abc", "OK", "Fail")
    ? 'Alltrim("abcdefghab", 1, "AB", "B") == "cdefgh"', IIF(Alltrim("abcdefghab", 1, "AB", "B") == "cdefgh", "OK", "Fail")
    ? 'Alltrim("abcdefghab", 1, "B", "AB") == "cdefgha"', IIF(Alltrim("abcdefghab", 1, "B", "AB") == "cdefgha", "OK", "Fail")
//    ? 'ISNULL(Rtrim(NULL))', IIF(ISNULL(Rtrim(NULL)), "OK", "Fail")

End Proc
Result:

Image

The functions set:

Code: Select all

Function Alltrim (Expression AS String) AS String

	Return _trim(.T., .T., Expression, 0, " ")

End Function

Function Alltrim (Expression AS String, Flags AS Int, TrimChars PARAMS String[]) AS String STRICT

	Return _trim(.T., .T., Expression, Flags, TrimChars)

End Function

Function Ltrim (Expression AS String) AS String

	Return _trim(.T., .F., Expression, 0, " ")

End Function

Function Ltrim (Expression AS String, Flags AS Int, TrimChars PARAMS String[]) AS String STRICT

	Return _trim(.T., .F., Expression, Flags, TrimChars)

End Function

Function Rtrim (Expression AS String) AS String

	Return _trim(.F., .T., Expression, 0, " ")

End Function

Function Rtrim (Expression AS String, Flags AS Int, TrimChars PARAMS String[]) AS String STRICT

	Return _trim(.F., .T., Expression, Flags, TrimChars)

End Function


STATIC Function _trim (TrimLeft AS Boolean, TrimRight AS Boolean, Expression As String, Flags As Int, TrimChars PARAMS String[]) AS String STRICT

    LOCAL parmNdx AS Int
    LOCAL Trimmed = .T. AS Boolean
    LOCAL LRTrimmed AS Int
    LOCAL comparison = StringComparison.Ordinal
    LOCAL compared AS String

    IF Expression = NULL
        Return Expression
    END IF

    IF Flags = 1
        comparison = StringComparison.OrdinalIgnoreCase
    END IF

    DO WHILE Trimmed

        Trimmed = .F.

        FOR parmNdx = 1 TO TrimChars:Length
            
            IF TrimLeft
                LRTrimmed = 0
 
                compared = TrimChars[parmNdx]
                DO WHILE Expression:StartsWith(compared, comparison)
                    LRTrimmed = compared:Length
                    compared = String.Concat(compared, TrimChars[parmNdx])
                END DO
                IF LRTrimmed > 0
                    Expression = Expression:Substring(LRTrimmed)
                    Trimmed = .T.
                END IF
            END IF

            IF TrimRight
                LRTrimmed = 0

                compared = TrimChars[parmNdx]
                DO WHILE Expression:EndsWith(compared, comparison)
                    LRTrimmed = compared:Length
                    compared = String.Concat(compared, TrimChars[parmNdx])
                END DO
                IF LRTrimmed > 0
                    Expression = Expression:Substring(0, Expression:Length - LRTrimmed)
                    Trimmed = .T.
                END IF
            END IF

        NEXT
 
    END DO

    Return Expression

End Function

VFP *TRIM() functions

Posted: Thu May 21, 2020 3:17 pm
by Chris
Hi Antonio,

Thanks a lot for your contribution! Will add it to the runtime, will just need to make small adjustments so that it can co-exist with the already existing implementation of AllTrim() etc. Fortunately the versions with no params at all seem to have the same behavior in both FoxPro and VO.

It should be possible to improve the performance by somehow avoiding creating many new strings while the function runs, but this can be improved later.

VFP *TRIM() functions

Posted: Fri May 22, 2020 10:03 am
by Karl-Heinz
Hi Antonio,

nice work and interesting to see how many trim options VFP offers !

About the Trim() function: Simply add it to your existing functions.

Code: Select all

FUNCTION Trim (Expression AS STRING) AS STRING
RETURN RTrim (Expression )  

FUNCTION Trim (Expression AS STRING, Flags AS INT, TrimChars PARAMS STRING[]) AS STRING STRICT
RETURN RTrim (Expression , Flags, TrimChars )
regards
Karl-Heinz

VFP *TRIM() functions

Posted: Fri May 22, 2020 1:19 pm
by atlopes
Thank you, Chris & Karl-Heinz. Like Matt, Thomas, Kevin, and others that are contributing to the X# VFP runtime, we hope that these steps may add to the potential of X# as a viable or interesting option for the VFP community, humble as they might be.

Before moving to something else, I reduced a little bit the need for the creation of new strings:

Code: Select all

STATIC Function TrimHelper (TrimLeft AS Boolean, TrimRight AS Boolean, Expression As String, Flags As Int, TrimChars PARAMS String[]) AS String STRICT

    LOCAL parmNdx AS Int
    LOCAL Trimmed = .T. AS Boolean
    LOCAL LRTrimmed AS Int
    LOCAL comparison = StringComparison.Ordinal AS System.StringComparison
    LOCAL compared AS String

    IF Expression = NULL
        Return Expression
    END IF

    IF Flags = 1
         comparison = StringComparison.OrdinalIgnoreCase
    END IF

    DO WHILE Trimmed

        Trimmed = .F.

        FOR parmNdx = 1 TO TrimChars:Length
 
            compared = TrimChars[parmNdx]

            IF TrimLeft
                LRTrimmed = 0
 
                DO WHILE String.Compare(Expression, LRTrimmed, compared, 0, compared:Length, comparison) = 0
                    LRTrimmed += compared:Length
                END DO
                IF LRTrimmed > 0
                    Expression = Expression:Substring(LRTrimmed)
                    Trimmed = .T.
                END IF
            END IF

            IF TrimRight
                LRTrimmed = Expression:Length - compared:Length

                DO WHILE LRTrimmed >= 0 AND String.Compare(Expression, LRTrimmed, compared, 0, compared:Length, comparison) = 0
                    LRTrimmed -= compared:Length
                END DO
                IF LRTrimmed < (Expression:Length - compared:Length)
                    Expression = Expression:Substring(0, LRTrimmed + compared:Length)
                    Trimmed = .T.
                END IF
            END IF

        NEXT
 
    END DO

    Return Expression

End Function

VFP *TRIM() functions

Posted: Fri May 22, 2020 3:48 pm
by Chris
Hi Antonio,

Thanks, looks perfect to me now!
I have updated the source in Git.