Logo

Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.

AT(), ATC(), RAT(), $

Did you ever need to know whether a string contains a particular substring? Say you have a memo field containing notes about a phone conversation and you want to see whether you discussed the “Super Duper Pooper Scooper.” Enter this group of functions.

Technically, $ is an operator, while AT(), ATC() and RAT() are functions. But all of them are used to find a substring within a string. $ simply indicates whether or not the substring is there. The others all return the position where the string was found.

Usage

lIsItInThere = cSearchFor $ cSearchIn
nFoundPos = AT( cSearchFor, cSearchIn [ ,nOccurrence ])
nFoundPos = ATC( cSearchFor, cSearchIn [ ,nOccurrence ])
nFoundPos = RAT( cSearchFor, cSearchIn [ ,nOccurrence ])

AT() and RAT() are case-sensitive; ATC() is not (the “C” stands for “case,” which seems backward to us, but at least the function exists). AT() and ATC() begin their search at the left-hand side of the string, while RAT() begins at the right (you can read it as “Right AT”).

Watch out for one thing with RAT(). Even though it searches from the right, the position it returns is measured from the left. We guess that’s the “RAT trap.”

All three functions take an optional parameter indicating which instance of the string to search for, so you can find, say, the third use of “Super Duper Pooper Scooper” rather than the first or last.

You can use these functions together with LEFT(), RIGHT() and SUBSTR() to parse a string into its component parts. This is often useful when you inherit data that hasn’t been properly normalized. For example, you might need to convert a single name field into first and last name fields or to pull apart an address field into street address, city, state and zip code.

Be aware that these functions can’t be optimized by Rushmore (unless you have some pretty unusual and not terribly useful index tags), so you won’t want to use them heavily for lookups. For example, don’t store several codes in a single field and plan to use $ to see if a specific code is present; normalize the data instead.

Example

lFoundIt = "Super Duper Pooper Scooper" $ mNotes
nStartPos = AT("Super Duper Pooper Scooper", mNotes)
nStartPos = ATC("Super Duper Pooper Scooper", mNotes)
nLastOne = RAT("Super Duper Pooper Scooper", mNotes)

* Here's a more useful example which shows how you'd take apart
* a field containing city, state and zip code to create separate
* fields. You'd call this routine like this:
STORE "" TO cCity, cState, cZip
DO PARSADDR WITH cCityStZip, cCity, cState, cZip

* parsaddr.prg
* Parse single address variable into city, state and zip
* Assumes parameter cAddress has structure:
*    City, ST Zip
* Zip can be either 5 or 10 digit

LPARAMETERS cAddress, cCity, cState, cZip

LOCAL nStartZip, nStartState

nStartZip = RAT(" ",TRIM(cAddress))
cZip = TRIM(SUBSTR(cAddress, nStartZip+1))

nStartState = AT(",", cAddress)
cState = ALLTRIM(SUBSTR(cAddress, nStartState+1, ;
                 nStartZip-nStartState-1))

cCity = ALLTRIM(LEFT(cAddress, nStartState-1))

RETURN

VFP 3.0b added double-byte versions of the functions in this group: AT_C(), ATCC(), RATC(). They’re the ones to use when you’re working in a language with double-byte characters or if there’s a chance your application will go international.

See Also

AllTrim(), AtLine(), AtCLine(), At_C(), AtCC(), Left(), Occurs(), RatC(), RatLine(), Right(), Substr(), Trim(), Upper()