String Functions

AddASCII

AfterAtNum

AllTrim
Asc

ASCIISum

ASCPos
At

AtAdjust

AtNum
AtRepl
AtToken

BeforAtNum

Chr

CharAdd
CharAnd
CharEven
CharHist
CharList
CharMirr
CharMix
CharNoList
CharNot
CharOdd
CharOne
CharOnly
CharOr
CharPix
CharRela
CharRelRep
CharRem
CharRepl
CharRLL
CharRLR
CharSHL
CharSHR
CharSList
CharSort
CharSub
CharSwap
CharWin
CharXOR

CountLeft
CountRight
Descend
Empty
hb_At
hb_RAt
hb_ValToStr
IsAlpha
IsDigit
IsLower
IsUpper

JustLeft
JustRight

Left
Len
Lower
LTrim

NumAt
NumToken
PadLeft
PadRight

PadC
PadL
PadR

POSALPHA
POSCHAR
POSDEL
POSDIFF
POSEQUAL
POSINS
POSLOWER
POSRANGE
POSREPL
POSUPPER

RangeRem
RangeRepl

RAt

RemAll

RemLeft
RemRight
ReplAll

Replicate

ReplLeft

ReplRight

RestToken

Right
RTrim

SaveToken

SetAtLike
Space
Str

StrDiff

StrFormat

StrSwap

StrTran
StrZero
SubStr

TabExpand
TabPack

Token

TokenAt
TokenEnd
TokenExit
TokenInit
TokenLower
TokenNext
TokenNum
TokenSep
TokenUpper

Transform
Trim
Upper
Val

ValPos
WordOne
WordOnly
WordRem
WordRepl
WordSwap

WordToChar


Empty()

EMPTY()

Checks if the passed argument is empty.

Syntax

      EMPTY( <xExp> ) --> lIsEmpty

Arguments

<xExp> is any valid expression.

Returns

A logical value. It is true (.T.) if the passed argument is empty otherwise it is false (.F.).

Description

This function checks if an expression has empty value and returns a logical indicating whether it the expression is empty or not.

Note : Characters with ASCII code 9, 10 and 13 always treated as “white spaces”;

Examples

      ? EMPTY( "I'm not empty" )    // .F.

Tests

PROCEDURE Test()
         ? EMPTY( NIL )             // .T.
         ? EMPTY( 0 )               // .T.
         ? EMPTY( .F. )             // .T.
         ? EMPTY( "" )              // .T.
         ? EMPTY( 1 )               // .F.
         ? EMPTY( .T. )             // .F.
         ? EMPTY( "smile" )         // .F.
         ? EMPTY( Date() )          // .F.
         ? EMPTY( {} )              // .T.
         ? EMPTY( {''} )            // .F.
         ? EMPTY( {=>} ) )          // .T.
         ? EMPTY( CHR(8) )          // .F.
         ? EMPTY( CHR(9) )          // .T.
         ? EMPTY( CHR(10) )         // .T.
RETURN

Compliance

Clipper

Files

Library is rtl

Seealso

LEN()

FieldName()

FIELDNAME()

Return the name of a field at a numeric field location.

Syntax

      FIELDNAME/FIELD(<nPosition>) --> cFieldName

Arguments

<nPosition> Field order in the database.

Returns

<cFieldName> returns the field name.

Description

This function return the name of the field at the <nPosition>th position. If the numeric value passed to this function does not correspond to an existing field in the designated or selected work area, this function will return a NULL byte.

Examples

      PROCEDURE Main()
         LOCAL x
         USE tests NEW
         FOR x := 1 TO tests->( FCOUNT() )
            ? "Field Name", FieldName( x )
         NEXT
         USE
         RETURN

Compliance

Clipper

Files

Library is rdd

Seealso

DBSTRUCT(), FCOUNT(), LEN(), VALTYPE()

ATail()

 

ATAIL()

Returns the rightmost element of an array

Syntax

      ATAIL( <aArray> ) --> Element

Arguments

<aArray> is the array.

Returns

<Element> the expression of the last element in the array.

Description

This function return the value of the last element in the array named <aArray>. This function does not alter the size of the array or any of the subscript values.

Examples

      LOCAL aArray := { "Harbour", "is", "Supreme", "Power" }
      ? ATail( aArray ) // Result is "Power"

Compliance

Clipper

Files

Library is vm

Seealso

LEN(), ARRAY(), ASIZE(), AADD()

SP_AMATCHES

AMATCHES()

  Short:
  ------
  AMATCHES() Counts the matches of an array with condition

  Returns:
  --------
  <nMatches> => Number of matches

  Syntax:
  -------
  AMATCHES(aTarget,[bCondition])

  Description:
  ------------
  <aTarget> is the target array. Normally an array of
  numeric values.

  [bCondition] is an optional codeblock used to select
  a subset of the array. This could be used to filter out 0's or
  non-numeric elements. The block must accept an array element as
  a parameter, and return true or false <expL> to determine if
  this element is part of the desired subset.

  Without [bCondition], the length of the array is
  returned.

  Examples:
  ---------
   v := AMATCHES(aSales)
   v := AMATCHES(aSales,{|e|valtype(e)=="N".and.e<>0})
   v := AMATCHES(aSales,{|e|valtype(e)=="C".and.e=="D"})

  Source:
  -------
  S_ASTATS.PRG

 

SP_ALENG

ALENG()

  Short:
  ------
  ALENG() Actual length of an array, less trailing nil elements

  Returns:
  --------
  <aLength>  => Actual array length, less trailing NILs

  Syntax:
  -------
  ALENG(aTarget)

  Description:
  ------------
  Determines the actual length of <aTarget>, less
  trailing nils.

  Examples:
  ---------

   a := {1,2,3,4,5,nil,nil,nil}
   ?len(a)       // => returns 8
   ?aleng(a)     // => returns 5

  Notes:
  ------
  This was a C function in prior Super.Libs

  Source:
  -------
  S_ALENG.PRG

 

SP_AFLENSX

AFLENSX()

  Short:
  ------
  AFLENSX() Returns an array of field LENGTHS for current dbf

  Returns:
  --------
  <aFields> => an array of field LENGTHS for the
  current dbf

  Syntax:
  -------
  AFLENSX()

  Description:
  ------------
  AFLENSX() creates and returns an array of field
  lengths from the current dbf. Unlike AFIELDS(), it does not
  require an initialized array beforehand.

  Examples:
  ---------
   aArray := AFIELDSX()
   aTypes := AFTYPESX()
   aLens  := AFLENSX()
   aDeci  := AFDECIX()

   if (nSelect := mchoice(aArray,10,10,20,20,"Pick Field")) >  0
      ?"Field "+aArray[nSelect]+" was selected"
      ?" of type     "+aTypes[nSelect]
      ?" of length   "+aLens[nSelect]
      ?" of decimals "+aDeci[nSelect]
   endif

  Source:
  -------
  S_AFTYPE.PRG

 

C5_LEN

 LEN()
 Return the length of a character string or the number of elements in an array
------------------------------------------------------------------------------
 Syntax

     LEN(<cString> | <aTarget>) --> nCount

 Arguments

     <cString> is the character string to count.

     <aTarget> is the array to count.

 Returns

     LEN() returns the length of a character string or the number of elements
     in an array as an integer numeric value.  If the character string is a
     null string ("") or the array is empty, LEN() returns zero.

 Description

     LEN() is a character and array function that returns the length of a
     character string or the number of elements in an array.  With a
     character string, each byte counts as one, including an embedded null
     byte (CHR(0)).  By contrast, a null string ("") counts as zero.

     For an array, LEN() returns the number of elements.  If the array is
     multidimensional, subarrays count as one element.  This means that the
     LEN() of a nested or multidimensional array simply returns the length of
     the first dimension.  To determine the number of elements in other
     dimensions, use LEN() on the subarrays as shown in the example below.
     Note that nested arrays in Clipper need not have uniform dimensions.

 Examples

     .  These examples demonstrate LEN() with various arguments:

        ? LEN("string of characters")         // Result: 20
        ? LEN("")                              // Result: 0
        ? LEN(CHR(0))                        // Result: 1
        //
        LOCAL aTest[10]
        ? LEN(aTest)                           // Result: 10

     .  This example creates a literal two-dimensional array, and then
        returns the number of elements in the subarray contained in the first
        element of the original array:

        LOCAL aArray := { {1, 2}, {1, 2}, {1, 2} }
        ? LEN(aArray)                        // Result: 3
        ? LEN(aArray[1])                     // Result: 2

     .  This example navigates a multidimensional array using LEN():

     LOCAL aArray := { {1, 2}, {1, 2}, {1, 2} }
        LOCAL nRow, nColumn, nRowCount, nColumnCount

        //
        nRowCount = LEN(aArray)
        FOR nRow = 1 TO nRowCount
           nColumnCount = LEN(aArray[nRow])
           FOR nColumn = 1 TO nColumnCount
              ? nRow, nColumn, aArray[nRow][nColumn]
           NEXT
        NEXT

     .  In this example a function returns an array of numeric values
        that describe the dimensions of a nested or multidimensional array.
        The function assumes that the array has uniform dimensions:

        FUNCTION Dimensions( aArray )
           LOCAL aDims := {}
           DO WHILE ( VALTYPE(aArray) == "A" )
              AADD( aDims, LEN(aArray) )
              aArray := aArray[1]
           ENDDO
           RETURN (aDims)

 Files   Library is CLIPPER.LIB.

See Also: AADD() ASIZE() LTRIM() RTRIM()

 

Hash vs Table

Consider a table for customers records with two character fields : Customer ID and customer name:

Cust_ID Cust_Name
CC001 Pierce Firth
CC002 Stellan Taylor
CC003 Chris Cherry
CC004 Amanda Baranski

 It’s known all possible and necessary operations on a table: APPEND, DELETE, SEEK and so on; by the way, for SEEK we need an index file also.

Listing this table is quite simple:

USE CUSTOMER
WHILE .NOT. EOF()
   ? CUST_ID, CUST_NAME
   DBSKIP()
ENDDO

 If our table is sufficiently small, we can find a customer record without index and SEEK :

LOCATE FOR CUST_ID = “CC003”
? CUST_ID, CUST_NAME

If we want all our data will stand in memory and we could manage it more simple and quick way, we would use an array ( with some considerations about size of table; if it is too big, this method will be problematic ) :

aCustomer := {}    // Declare / define an empty array
USE CUSTOMER
WHILE .NOT. EOF()
   AADD(aCustomer, { CUST_ID, CUST_NAME } )
   DBSKIP()
ENDDO
Traversing this array is quite simple :

FOR nRecord := 1 TO LEN( aCustomer )

    ? aCustomer[ nRecord, 1 ], aCustomer[ nRecord, 2 ]
NEXT
or :

a1Record := {}

FOR EACH a1Record IN aCustomer
   ? a1Record[ 1 ], a1Record[ 2 ]
NEXT

And locating a specific record too:

nRecord := ASCAN( aCustomer, { | a1Record | a1Record[ 1 ] == “CC003” } )

? aCustomer[ nRecord, 1 ], aCustomer[ nRecord, 2 ]

A lot of array functions are ready to use for maintain this array : ADEL(), AADD(), AINS() etc …

Now, let’s see how we could use a hash for achieve this job :

hCustomer := { => } // Declare / define an empty hash

USE CUSTOMER
WHILE .NOT. EOF()
   hCustomer[ CUST_ID ] := CUST_NAME
   DBSKIP()
ENDDO
Let’s traversing :

h1Record := NIL

FOR EACH h1Record IN hCustomer
   ? h1Record: __ENUMKEY(),h1Record:__ENUMVALUE()
NEXT

Now, we have a bit complicate our job; a few field addition to the table :

No: Field Name Type Width  Dec Decription

1

 CUST_ID

C

 5

0

Id ( Code )

2

 CUST_NAME

C

10

0

Name

3

 CUST_SNAM

C

10

0

Surname

4

 CUST_FDAT

D

 8

0

First date

5

 CUST_ACTV

L

 1

0

Is active ?

6

 CUST_BLNCE

N

11

2

Balance

 While <key> part of an element of a hash may be C / D / N / L type; <xValue> part of hash too may be ANY type of data, exactly same as arrays.

So, we can make fields values other than first ( ID) elements of an array:

hCustomer := { => } // Declare / define an empty hash
USE CUSTOMER
WHILE .NOT. EOF()
   a1Data:= { CUST_NAME, CUST_SNAM, CUST_FDAT, CUST_ACTV, CUST_BLNCE }
   hCustomer[ CUST_ID ] := a1Data
   DBSKIP()
ENDDO
Let’s traversing :

h1Record := NIL

FOR EACH h1Record IN hCustomer
   a1Key  := h1Record:__ENUMKEY()
   a1Data := h1Record:__ENUMVALUE()
   ? a1Key
   AEVAL( a1Data, { | x1 | QQOUT( x1 ) } )
NEXT
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
/*
Hash vs Tables
 
*/
#define NTrim( n ) LTRIM( STR( n ) )
#define cLMarj SPACE( 3 )
PROCEDURE Main()

  SET DATE GERM
  SET CENT ON
  SET COLO TO "W/B"
  SetMode( 40, 120 )
 
  CLS
 
  hCustomers := { => } // Declare / define an empty PRIVATE hash
 
  IF MakUseTable() 
 
     Table2Hash()
 
     * Here the hash hCustomers may be altered in any way
 
     ZAP
 
     Hash2Table()
 
  ELSE
      ? "Couldn't make / USE table"
  ENDIF
 
  ?
  @ MAXROW(), 0
  WAIT "EOF HashVsTable.prg"
 
RETURN // HashVsTable.Main()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.
PROCEDURE Table2Hash()
   hCustomers := { => } 
   WHILE .NOT. EOF()
     hCustomers[ CUST_ID ] := CUST_SNAM
     DBSKIP()
   ENDDO
 
   ListHash( hCustomers, "A hash transferred from a table (single value)" )
 
   hCustomers := { => } // Declare / define an empty hash
   DBGOTOP()
   WHILE .NOT. EOF()
      hCustomers[ CUST_ID ] := { CUST_NAME, CUST_SNAM, CUST_FDAT, CUST_ACTV, CUST_BLNCE }
      DBSKIP()
   ENDDO
 
   ListHash( hCustomers, "A hash transferred from a table (multiple values)" )
 
RETURN // Table2Hash()

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

PROCEDURE Hash2Table()
   LOCAL h1Record,;
         c1Key,;
         a1Record,;
         n1Field
 
   FOR EACH h1Record IN hCustomers
      c1Key := h1Record:__ENUMKEY()
      a1Record := h1Record:__ENUMVALUE()
      DBAPPEND()
      FIELDPUT( 1, c1Key )
      AEVAL( a1Record, { | x1, n1 | FIELDPUT( n1 + 1 , x1 ) } )
   NEXT h1Record
   DBGOTOP()
 
   ?
   ? "Data trasferred from hash to table :"
   ?
   WHILE ! EOF()
      ? STR( RECN(), 5), ''
      FOR n1Field := 1 TO FCOUNT()
         ?? FIELDGET( n1Field ), ''
      NEXT n1Field
      DBSKIP()
   ENDDO 
 
RETURN // Hash2Table()

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

PROCEDURE ListHash( hHash, cComment )
 
  LOCAL x1Pair
 
  cComment := IF( HB_ISNIL( cComment ), '', cComment )
 
  ? 
  ? cComment // , "-- Type :", VALTYPE( hHash ), "size:", LEN( hHash )
  ?
  IF HB_ISHASH( hHash ) 
     FOR EACH x1Pair IN hHash
        nIndex := x1Pair:__ENUMINDEX()
        x1Key := x1Pair:__ENUMKEY()
        x1Value := x1Pair:__ENUMVALUE()
        ? cLMarj, NTrim( nIndex ) 
*       ?? '', VALTYPE( x1Pair )
        ?? '', x1Key, "=>"
*       ?? '', VALTYPE( x1Key ) 
*       ?? VALTYPE( x1Value ) 
        IF HB_ISARRAY( x1Value ) 
           AEVAL( x1Value, { | x1 | QQOUT( '', x1 ) } )
        ELSE 
           ?? '', x1Value
        ENDIF 
     NEXT
  ELSE
    ? "Data type error; Expected hash, came", VALTYPE( hHash ) 
  ENDIF HB_ISHASH( hHash )
RETURN // ListHash()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

FUNCTION MakUseTable() // Make / USE table
 
 LOCAL cTablName := "CUSTOMER.DBF"
 LOCAL lRetval, aStru, aData, a1Record 
 
 IF FILE( cTablName ) 
    USE (cTablName)
 ELSE
    aStru := { { "CUST_ID", "C", 5, 0 },;
               { "CUST_NAME", "C", 10, 0 },;
               { "CUST_SNAM", "C", 10, 0 },;
               { "CUST_FDAT", "D", 8, 0 },;
               { "CUST_ACTV", "L", 1, 0 },;
               { "CUST_BLNCE", "N", 11, 2 } }
    * 
    * 5-th parameter of DBCREATE() is alias - 
    * if not given then WA is open without alias 
    *                              ^^^^^^^^^^^^^ 
    DBCREATE( cTablName, aStru, , .F., "CUSTOMER" ) 
 
    aData := { { "CC001", "Pierce", "Firth", 0d20120131, .T., 150.00 },; 
               { "CC002", "Stellan", "Taylor", 0d20050505, .T., 0.15 },;
               { "CC003", "Chris", "Cherry", 0d19950302, .F., 0 },;
               { "CC004", "Amanda", "Baranski", 0d20011112, .T., 12345.00 } }
 
    FOR EACH a1Record IN aData
        CUSTOMER->(DBAPPEND())
        AEVAL( a1Record, { | x1, nI1 | FIELDPUT( nI1, X1 ) } )
    NEXT a1Record 
    DBGOTOP()
 
 ENDIF 
 
 lRetval := ( ALIAS() == "CUSTOMER" )
 
RETURN lRetval // MakUseTable()

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
 
HashVsTable