VFP *TRIM() functions

This forum is meant for questions about the Visual FoxPro Language support in X#.

Post Reply
atlopes
Posts: 83
Joined: Sat Sep 07, 2019 11:43 am
Location: Portugal

VFP *TRIM() functions

Post 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
User avatar
Chris
Posts: 4906
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

VFP *TRIM() functions

Post 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.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
Karl-Heinz
Posts: 774
Joined: Wed May 17, 2017 8:50 am
Location: Germany

VFP *TRIM() functions

Post 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
atlopes
Posts: 83
Joined: Sat Sep 07, 2019 11:43 am
Location: Portugal

VFP *TRIM() functions

Post 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
User avatar
Chris
Posts: 4906
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

VFP *TRIM() functions

Post by Chris »

Hi Antonio,

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

XSharp Development Team
chris(at)xsharp.eu
Post Reply