Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Searching for range in a String 3

Status
Not open for further replies.

Scott24x7

Programmer
Jul 12, 2001
2,826
JP
Hi All,
I'm having a problem trying to search a string to determine the presense of a value, which could be a range.
So for instance, if I have the following string:

A23: M2 123 456 7890

And I want to determine if it contains, non-numeric data (so the A and the M as well as the : would be identified, but I don't need to know the specific character.

I've tried several combinations of OCCURS, AT, BETWEEN CHR() but can't seem to get one that works, due to the "Range" at hand.

If we called the string lcText My expression looked something like:

lnRetVal = OCCURS(BETWEEN(lcText,CHR(65),CHR(90)),lcText)
lnRetVal = AT(BETWEEN(lcText,CHR(65),CHR(90)),lcText,1)

(in these cases I was just looking for Uppercase Alpha as that will do)

But they fail, and the BETWEEN I think is the problem, but I don't see any other function that would do it?
Is this possible, or do I need to tear the whole string down byte by byte and examine its ASC value?

I tested with both to try to get BETWEEN to work with the string search, but what I'm really after is the AT() position of the first occurrence of a "alpha" character.
I realize in this example, I should be using UPPER(lcText), but the string I was testing with was already uppercase, so I was just trying to make it work in test environment before I put it in the code.

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Unless you use regular expressions - which isn't something built into fox, you're going to have to parse the string manually.
You can still use BETWEEN() though. Like:
Code:
STORE LEN(string) TO nLen
FOR ndx = 1 to nLen
   IF BETWEEN(SUBSTR(string, ndx, 1), "A", "Z")
      whatever...
   ENDIF
NEXT


-Dave Summers-
[cheers]
Even more Fox stuff at:
 
Dave,
Thanks for that. I thought maybe I just wasn't getting the order right on ranges for values. For a lot of other things I already "tear them apart and rebuild them" so I'm used to that. Was just hoping for this casee there was a nice elegant "1 line" option, but since there's not, no worries. I'll just deconstruct them.
That usage of IF BETWEEN though may work for me, and not as "manual" as testing ASC() Values of each item one at a time. What I need is the character position so that I can issue a LEFT() or RIGHT() on the overall string to cut off the parts I don't want. But with this, I can figure it out. Thanks.
Cheers

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Dave, this worked a treat.
Many thanks.

One question though, you use:

STORE LEN(string) to nLen

Where I would have just issued:Z

lnStrLen = LEN(string)

Is this just a "style" issue, or is there some greater benefit to STORE here I'm missing? (Is it just considered "better readability" from a code standpoint?)

It's been a while since I did any significant development (about 15 years) and just getting back into things. So I'm curious to learn the "nuance" of these kinds of things.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
It's a style issue (or religious war) and has been since dBaseII was new.

There is no functional difference between STORE and direct assignment for a single string.
 
Religious war... LOL now that is an analogy I certainly understand.
I guess I'm er, not STOREish.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Don't give up on STORE. It can be useful for multiple assignments.

Code:
STORE 0 to nStart, nEnd, nMiddle
There have also been performance tests along the years that show a preference to one over the other, but I've never been swayed either way.

I don't chase milliseconds in most situations. I'll use the one that makes the most sense and results in the most clarity in my current situation and environment every time.
 
Dan,
I see how handy that is. I don't often need to set multiple values of the same value, but starting with a 1 or a 0 or a "" I can see usefulness there.

I have only chased milliseconds when it matters on a LOOP that gets hit thousands of times in a process (like large batch processing records, or updating indexes). But it doesn't seem STORE would be much use there unless those values get reset at the start of a new break point of some time.

Readability, I can accept either way. For some reason, I guess I'm old, and goes all the way back to my earliest development language days (COBOL, RPG, FORTRAN) assignment just "comes natural".
-S

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Don't forget the CHRTRAN() trick
Code:
LOCAL lcString
lcString = "A23: M2 123 456 7890"
IF LEN(m.lcString) == LEN(CHRTRAN(m.lcString,"ABCDEFGHIJKLMNOPQRSTUVWXYZ",""))
	MESSAGEBOX("No captial letters")
ELSE
	MESSAGEBOX("At least one captial letter")
ENDIF

* or
IF LEN(CHRTRAN(m.lcString,"0123456789","")) = 0
	MESSAGEBOX("Only digits")
ELSE
	MESSAGEBOX("At least one character is not a digit")
ENDIF

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Vilhelm, that is very clever... never thought of doing a check that way.

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Vilhelm,
Just want to say, I've applied your methodology to some other conversions I have been working on, and it has been brilliant! Great time saver, thanks very much for this tip!

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Scott,

The main reason I use STORE is just to initialize a variable to a particular data type. It makes it easier for me personally to debug code. I can refer to the top of my code segment and see at a glance what the type is. Or I can look over in the 'locals' pane and see it at a glance. Yes, I have had to endure ridicule for wasting lines of code, but oh well. [smile]
Using type notation like you did is ideal too; such as 'n' for numeric, 'c' for character, and so on, if everyone does it all the time. I have been bitten by trying to assign something to a variable with a different data type, so it's just what I do now.



-Dave Summers-
[cheers]
Even more Fox stuff at:
 
Dave,
Thanks for the explanation. I do see why you find it useful. As you mention I do use a convention that kind of does the same thing (so long as I don't confuse a logical for a numeric, as happens from time to time) Like I think "OCCURS" will return a .T./.F. but it returns a numeric instead.
But yes, way way back in my VFP 3 days I remember after attending DevCon in Phoenix the discussion amoung "NEW" OOP'ers and moving from the m.<variable> thinking to "new" OOP methods, and the notion was:
l = local
g = global
p = private
as leading variable names, then 2nd character data type
c = character
n = numeric
l = logical, etc
being the main types.

The only "danger" I ever found in that (and I don't think STORE solves it either) is just "naming" them that way doesn't make it true! (i.e. g preface does not ACTUALLY make the variable GLOBAL so you have to declare it global if you actually want it to be. But that has rarely been an issue.
Returning to serious development after more than a decade, I'm happy that "convention" still comes naturally. It's good to see that a good habit learned early sticks later as "automatic".


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
FWIW, even if you use a naming convention, it's still a best practice to use m. anywhere that you refer to a variable. So, in my code, you'll see:

Code:
nTotal = m.nTotal + m.nValue

Etc.

Since VFP can't enforce a naming convention, you can't be sure that there won't be a field with the same name as a variable.

Tamar
 
Tamar,
That's interesting... I remember years ago the push to "drop" the m. convention (back around VFP3 or 5, can't remember which now).
Has that been "reversed" or just one of those hotly debated style issues? I remember taking it out of all my old converted Fox2.x code back around... 2001.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
I think you may misremember some things. There has never been a "push" to disavow core behaviors of the language.

Using m. is properly scoping a memory variable. Plain and simple. It's core Xbase behavior dating back to the earliest days and has not changed.

For a long time, people thought this was a bug:

a = createobject("form")
a.Show() && error!

The error comes from the fact that "a" is the first workarea and the workarea always takes precedence. It has always been the first workarea and has always taken precedence and it's still the first workarea and still takes precedence.

This works fine, though:

a = createobject("form")
m.a.Show()

Using m. properly scopes a reference to a memory variable. You should use it when you need to refer to a memory variable. That is its meaning and nothing more, but it can mean so much when you need it to mean that.

That part of the language has never changed.
 
What has been dropped is using SCATTER/GATHER MEMVAR and binding controls to memvars because buffering does that better.

Bye, Olaf.
 
vgulielmus

I would use this instead of your code:

[tt]IF [highlight #FCE94F]EMPTY[/highlight](CHRTRAN(m.lcString,"0123456789",""))[/tt]
[tt]MESSAGEBOX("Only digits")[/tt]​
[tt]ELSE[/tt]
[tt]MESSAGEBOX("At least one character is not a digit")[/tt]​
[tt]ENDIF[/tt]​

Works exactly the same way, just looks like cleaner code to me. Just my preference, of course.

mmerlinn


Poor people do not hire employees. If you soak the rich, who are you going to work for?

"We've found by experience that people who are careless and sloppy writers are usually also careless and sloppy at thinking and coding. Answering questions for careless and sloppy thinkers is not rewarding." - Eric Raymond
 
Interesting... Yes, I did drop the SCATTER/GATHER thing long ago, though on a few unusual occasions I remember using it, I think related to memo field data manipulation for something (but now I'm reaching back to that 15 year mark where I stopped being the man "Dev" and moved more into pure leadership). I still dabbled from time to time with some "apps around the house" but even that stopped about 10 years ago.

I do understand what you are saying about the difference between m.variable and variable, but that was (again, I remember hearing this at either 1996 or 1997 DevCon) the push instead to use a variable naming convention... prefacing variable names with l, p, g to show their scope, and then type c, n, l, etc. would yield then lnVariable or gcVariable which would be impossible to mistake as Variable (whether it came from a field, an array, memvar creation, etc.). And fields always prefaced with TABLE.FIELDNAME then there was no risk of lcVariable colliding with TABLE.FIELDNAME, or problems like a.SHOW() (as single character variables were a no-no.. it would at least be loA instead of just a. (I adopted CamelNotation back then too.)

Anyway, my quest here is more to understand "modern accepted style", as I know mine may now be out of date. And while I know I am crazy, I know I also remember this conversation/presentation in one of the sessions from DevCon in the past. But agree, that my memory may be hazy of that time. But I haven't used m.<anything> in a very long time, and last I did it had some very specific usage as well. I can see how m. makes it explicitly a memory variable though.

Olaf, you may have just triggered a memory of when I did that though... when I didn't want the control source to be the table, (maybe for some poorly conceived reason) I don't have that program any longer though, so I don't really recall now why I used but it was either to get MEMO data into something I needed, or maybe scattered into an Array for manipulation and then pulled back out...)

Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
1) Not using m. can slow down the code.
The amount of delay depends on number of cursor / table fields and the total number of memory variables (the same variable used more times increase the execution time)
Code:
LOCAL lnFields,lni,lnLoop,lnRetry,ttt,bb,aa
FOR lnFields = 10 TO 50 STEP 10
	CLEAR
	LOCAL laFields[m.lnFields,18]
	laFields = '
	FOR lni = 1 TO m.lnFields
		laFields[m.lni,1] = 'Field'+TRANSFORM(m.lni)
		laFields[m.lni,2] = 'C'
		laFields[m.lni,3] = 10
		STORE 0 TO laFields[m.lni,4], laFields[m.lni,17], laFields[m.lni,18]
		STORE .F. TO laFields[m.lni,5], laFields[m.lni,6]
	NEXT

	CREATE CURSOR cc FROM ARRAY laFields

	lnLoop = 1000000
	? TRANSFORM(m.lnFields), 'Fields'
	? TRANSFORM(m.lnLoop), 'Loops'
	? 'One memory variable in expression'
	FOR lnRetry = 1 TO 10
		ttt = SECONDS()
		FOR lni = 1 TO m.lnLoop
			bb = aa
		NEXT
		? 'without m.',SECONDS() - m.ttt

		ttt = SECONDS()
		FOR lni = 1 TO m.lnLoop
			bb = m.aa
		NEXT
		? 'm.',SECONDS() - m.ttt
	NEXT
	? '10 memory variables in expression'
	FOR lnRetry = 1 TO 10
		ttt = SECONDS()
		FOR lni = 1 TO m.lnLoop
			bb = aa or aa or aa or aa or aa or aa or aa or aa or aa or aa
		NEXT
		? 'without m.',SECONDS() - m.ttt

		ttt = SECONDS()
		FOR lni = 1 TO m.lnLoop
			bb = m.aa or m.aa or m.aa or m.aa or m.aa or m.aa or m.aa or m.aa or m.aa or m.aa
		NEXT
		? 'm.',SECONDS() - m.ttt
	NEXT
	WAIT
NEXT

2) In many situations, EMPTY() can be used instead of LEN() = 0
But they are not identical
Code:
yy = space(2)
?EMPTY(m.yy) && .T.
?LEN(m.yy) = 0 && .F.

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top