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 Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

length of string to left of nth word

Status
Not open for further replies.

AlastairP

Technical User
Feb 8, 2011
286
AU
I need to find the length of the characters in a string left of a found word using GETWORDNUM

Code:
lnWcnt=GETWORDCOUNT(lcLine)
lnChr=0
lcFoundNext=.f.
FOR i = 1 TO lnWcnt
	lcCheckWord = GETWORDNUM(lcLine,i)
	SELECT cWrongWords 
	GO TOP 
	SCAN 
	   IF ALLTRIM(cWord) = lcCheckWord 
	     lcFoundNext=.t.
	     EXIT
	   ENDIF 
	ENDSCAN 
	IF lcFoundNext=.t.
	   EXIT
	ENDIF
ENDFOR

When I get to this point, I need a character count (including spaces) left of the specific word number. I can't use AT or similar because I don't know how many occurances there are left of the specific word located with GETWORDNUM. The answer is probably staring me in the face, I just can't see the wood for the trees right now!
 
You can alter your loop to store the number of current occurrence.
Code:
CLEAR 
xx="Yuppi du yuppi du yuppi du / Yuppi du-i-du yuppi du / Yuppi du yuppi du yuppi du / Yuppi du-i-du yuppi du ...."
LOCAL lcWord,laWords[1,2]
FOR lni = 1 TO GETWORDCOUNT(xx)
	lcWord = GETWORDNUM(xx,lni)
	IF lni = 1
		laWords[1,1] = lcWord
		laWords[1,2] = 1 && current occurence
		? 'First word:',laWords[1,1]
	ELSE
		lnRow = ASCAN(laWords,lcWord,1,-1,1,2+4+8) 
		IF lnRow = 0
			DIMENSION laWords[ALEN(laWords,1) + 1 , 2]
			laWords[ALEN(laWords,1),1] = lcWord
			laWords[ALEN(laWords,1),2] = 1
			? 'New word:',laWords[ALEN(laWords,1),1]
		ELSE
			laWords[lnRow,2] = laWords[lnRow,2] + 1 && current occurence
			? 'New occurence of ',laWords[lnRow,1],laWords[lnRow,2]
		ENDIF
	ENDIF
NEXT

?'Total'
FOR lni = 1 TO ALEN(laWords,1)
	? laWords[lni,1],laWords[lni,2]
NEXT

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
The following is a pretty simplistic example.
Since we do not know what you want to do if you should encounter your 'target' word/phrase more than once, I will leave that up to you.

Code:
cFindWord = "of"
cString = "This is a test string of characters which are of interest here"

* --- Determine How Many Times Does The 'Target' Word/Phrase Occur In Test String ---
nWordOccurs = OCCURS(cFindWord,cString)
nFoundWordCnt = 0  && Counter for Word/Phrase Found
nCharCount = 0  && Character Counter

* --- Cycle Through The Words Until Last 'Target' Word/Phrase Found ---
nWordCount = GETWORDCOUNT(cString)
FOR WordCntr = 1 TO nWordCount
   cTestWord = GETWORDNUM(cString,WordCntr)

   IF cTestWord = cFindWord
      nFoundWordCnt = nFoundWordCnt + 1
      * --- Do Whatever Needed  With nCharCount ---
      * --- Depending on your needs you might write the 'find' to a data table ---
      * --- Then add its own length to the nCharCount ---
      * --- And continue looping ---
      * --- Or whatever else you need to do ---
      * < Do Whatever >
      IF nFoundWordCnt = nWordOccurs
         * --- Last Occurrence, Exit Loop ---
         EXIT
      ENDIF
   ELSE
      * --- Keep a running total of characters encountered so far ---
      nCharCount = nCharCount + LEN(cTestWord + " ")  && Don't forget to add in space between words
   ENDIF
ENDFOR

Good Luck,
JRB-Bldr
 
I need a character count (including spaces) left of the specific word number

Alternatively you can indeed use the AT() function.
Again the following is a very simplistic example:

Code:
cFindWord = "of"
cString = "This is a test string of characters which are of interest here"

* --- Determine How Many Times Does The 'Target' Word/Phrase Occur In Test String ---
nWordOccurs = OCCURS(cFindWord,cString)

IF nWordOccurs > 0
   FOR Cntr = 1 To nWordOccurs
     * --- Locate n-th occurrence within entire test string of 'target' word/phrase using the AT() function with the nOccurrence option ---
     nLoc = AT(cFindWord, cString, Cntr)
     * --- Create string of characters to Left of 'target' word/phrase (including spaces, etc.) ---
     cLeftStr = LEFT(cString,nLoc-1)  
     * --- Determine number of characters to left of n-th occurrence of 'target' word/phrase ---
     nLeftChar = LEN(cLeftStr)  
     * < Do Whatever needs to be done with this info >
     * --- Either continue for all occurrences of the 'target' word/phrase or Exit ---
   ENDFOR
ELSE
   * --- The 'target' Word/Phrase does NOT occur in the test string ---
   * < Do Whatever >
ENDIF

Good Luck,
JRB-Bldr
 
I still don't see what you're trying to accomplish.

You take a word from a text via getwordnum and compare it to a list of words in the cursor cWrongWords.
So you seem to look for wrong words.

If you find one you exit the inner and outer loop. In that case the word you found always will be the first occurrence of this word. So AT() is a solution. If the same word would already have been in the previous part of the text, you would have found it and exited the text parsing before.

But you say AT is not your solution, as the word might exist multiple times. Then you're wrong with exiting both loops.

What you could do instead of using GETWORDNUM and GETWORDCOUNT is split the text into a words array by ALINES. With ALINES options to separate at spaces or punctuation additional to line feeds you get words instead of lines, and other parameter options let ALINES include separators with the split words, so you don't loose any characters.

Now you can even put this array into a second cursor cWords and inner join this with cWrongwords. The elements you find in cWrongwords will have a left prefix of all previous array elements in the words array or records in the cWords cursor.

Bye, Olaf.
 
If I understand you correctly, you want the length of the string to the left of a particular word in the string. If so, I would do something like this (untested).

[tt]Sampleword = "string"[/tt]
[tt]Samplestring = "If I understand you correctly, you want the length of the string to the left of a particular word in the string."[/tt]

[tt]Leftstringlength = LEN(ATXLEFT(Samplestring, Sampleword, 1))[/tt]​

That would produce the length of "[tt]If I understand you correctly, you want the length of the [/tt]" which is 58 characters.

If you wanted the second occurrence of the [tt]Sampleword[/tt], then do this:

[tt]Leftstringlength = LEN(ATXLEFT(Samplestring, Sampleword, 2))[/tt]​

That would produce the length of "[tt]If I understand you correctly, you want the length of the string to the left of a particular word in the [/tt]" which is 98 characters.

Note that the [tt]ATXLEFT()[/tt] function does LEFT TRIM but does not RIGHT TRIM the string it extracts from [tt]Samplestring[/tt].

If you perchance wanted the length of the string between two words in the string, you would do this:

[tt]Sampleword = "string"[/tt]
[tt]Sampleword2 = "string"[/tt]
[tt]Samplestring = "If I understand you correctly, you want the length of the string to the left of a particular word in the string."[/tt]

[tt]Leftstringlength = LEN(ATXRIGHT(Samplestring, Sampleword, Sampleword2))[/tt]​

That would produce the length of "[tt] to the left of a particular word in the [/tt]" which is 41 characters.

Note that the [tt]ATXRIGHT()[/tt] function does not LEFT TRIM and does not RIGHT TRIM the string it extracts from [tt]Samplestring[/tt].

[tt]ATXLEFT()[/tt] and [tt]ATXRIGHT()[/tt] are UDF functions and the code is found in faq184-5975



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
 
Thankyou for your replies. Those functions are pretty cool mmerlinn, nice work.
I still have much to work on with this, and it looks like I will probably redesign the concept a bit until I get a good result.

What I am trying to achieve is to replace the visual portion of MSWord spell checker with a VFP one. The reason being, that in most cases, the msword spell check dialog does appear correctly in front of VFP, but sometimes it does not and the user has to click on the word icon in the task bar to bring it to front.
While most users learn this, occassionaly they have "Moments", where I can see their frustation and desk thumping with the comments "What's wrong with your program?"

So I if MSWord encounters spelling mistakes, I can generate a list of the mis-spelt words with:

Code:
FOR EACH loError IN loDoc.SpellingErrors
	lcWord=loError.Text
	lnWordID=lnWordID+1
	DIMENSION this.spellingerrorlist(lnWordID ,3)
	STORE lnWordID TO this.spellingerrorlist(lnWordID, 1) && count
	STORE lcWord TO this.spellingerrorlist(lnWordID, 2) && error	 
	STORE lcWord TO this.spellingerrorlist(lnWordID, 3) && word to replace with, for now, it is the error	
ENDFOR

For each mispelt word I can get a list of suggestions with:

Code:
	loSuggestions = oWrd.GetSpellingSuggestions(lcWord)
	lnCount = loSuggestions.count
	IF lnCount = 0
		&& if there are no suggestions, add this dummy line
		lnSuggestID = lnSuggestID+1		
		DIMENSION this.spellingsuggestionlist(lnSuggestID ,2)
	   	STORE 0  TO this.spellingsuggestionlist(lnSuggestID , 1) && 0
		STORE "No Suggestions" TO this.spellingsuggestionlist(lnSuggestID , 2) && No words	  
	ENDIF 			
	For each loSuggestion In loSuggestions
		lnSuggestID = lnSuggestID+1	
		lcSuggest= loSuggestion.Name
	   	DIMENSION this.spellingsuggestionlist(lnSuggestID ,2)
	   	STORE lnSuggestID TO this.spellingsuggestionlist(lnSuggestID , 1)&& suggestion id
		STORE lcSuggest TO this.spellingsuggestionlist(lnSuggestID , 2)	 && suggested word	  	   	
	   	
	EndFor

So I already have the list of spelling errors, then I have to step through the string in VFP visually for the user to interact with.
To achieve this, I SET MEMOWIDTH TO 60 and then display a few lines at a time and as each word is dealt with, until the end of the string.
That is why I was exiting the nested loops as I needed to get the next word in the list previously created.
I also learned that I had to strip certain characters from the string, like commas and fullstops as VFP would not always find the word identified by msword.

However, I think I will perhaps to do this another way . . .
Send the entire text to word to get an error count. If > 0, then continue otherwise quit
Then clear the entire text from MSword and send a line at a time to msword, if no errors are found, keep clearing the text and sending the next line until errors are found.
If errors are found on the line, step through the line a word at a time getting the match ups between the string and msword errors & get the user to do their corrections before continue looping through the lines. The purpose of identifying the number of character left of the specfic word was so I could accuratly place a visual indicator to identify the current word with the spelling mistake.








 
Well, I also have Fox Spell, which was OK, but the dictionary and suggested words were not as good as msword. So we have been using msword for a couple of years now. I contemplated reworking Fox Spell to use the msword dictionary, but I thought it would be just as easy to start from stratch.

While testing the code, I have identified something with AT() and ATC():


lcPhrase="abcde abcd abcd"

SET EXACT ON
ln=AT("abcd", lcPhrase,2)

returns the position of the first word, being "abcde" not the third word "abcd", being the second occurance of "abcd"





 
Well, AT is search for the string, not for whole words, as abcde also starts with abcd, the first whole word abcd is the second abcd.
Why don't you simply follow my post about using ALINES?

Bye, Olaf.
 
>I thought it would be just as easy to start from stratch.
In lieu of getting stuck at such a detail, do you still think that way? How about extending the FoxSpell checker instead. Yes, that means understanding its code. But if that is over your head you really think doing it all yourself is easier?

Bye, Olaf.
 
Doing it yourself is easier in the long run, because I get to understand how it all works, I do things the way I am used to. Solving things as I go helps me to learn more as I go along. When I get stuck like this, I just find another way.
While Fox Spell code is not hard to understand, its a little convoluted.
 
Maybe you mistake understanding its code with understanding the usage of it. If the usage would be complicated it would be a bad library.

The code itself is quite advanced, including computing similar words, etc. Convoluted? That's of course depending on your taste, but its designed good from what I see.

Yes, own code always has the advantage to be known by yourself, that's what I said with having to understand the code given by foxspellchcker, if you want to extend it the way you want it.

What does convoluted mean to you? It's good OOP design to construct in a hierarchic way from base to composition of all things involved. Spell checking logic is mainly in the spellcheck class and the dialog obviously the splchkdialog, which is composed of a few classes, eg splchkcontainer, which itself is composed of some classes. That's the hierachy.

As you are using GETWORD... functions you most probably haven't looked at the code very deeply, ALINES is used and that splits up text in sentences, just once. That's always better than using GETWORDNUM, as ALINES only will need to go from start to end once and generates the smaller pieces (sentences) for multiple future reference.

From your question I deduct you do a multiple pass of the text. pass1 with checking all words found by GETWORDNUM and putting wrong words into the cWrongWords cursor, then in pass2 you iterate all words again to mark all wrong words (?) Why not directly in pass1, then, while you're at it?

Bye, Olaf.
 
It's quite easy to modify the FoxSpell dictionary - and you don't need to understand the inner working of FoxSpell in order to do so. In fact, I think it wouild be fairly easy to incorporate your existing Word custom dictionary into the FoxSpell dictionary (although I've never tried to do that myself).

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Alright, you have convinced me . . . I will look again at fox spell and see if I can use the msword dictionary.
FYI Olaf, this was how I was doing it:

I used msword to get a count of the spelling errors on the entire text, if there are errors, then:
Split the text into lines using MLINES with memowidth of 65. Removed any blank lines.
Looped through the lines calling MSWORD to do a count of the errors, if encounters errors, then exit loop at that line.
Stored current line number so when finished spelling the line, call the looping code again to continue on checking the next lines

MSword gives me the list of the spelling errors for that line. MSWORD gives me a list of suggestions for each word.
Run the error list for this line through a loop once to get the number of times each error occurs in this line, and its postion using AT()
The postion is used to highlight the current word being corrected.
If the word identified by msword can't be found by VFP in the current line, I remove fullstops and commas, once I get a match, I store the match to an array including the original msword word & the original VFP word (So I can restore the full stops and commas later)

Once completed all lines, use STRTRAN()to do the replacements in the original text if the source was from a text box or edit box.
If the source was HTML, the text string was created from "innertext", To apply the spelling changes to the original HTML was going to be the next step. This is why I chose to create an array of the spelling mistakes and store the corrections and matches for later.

I had pretty much nailed the entire thing, other than identifying the postion of each word in the current line to enable the marking of the current word visually. The text lines displayed in the same manner as fox spell, using labels.
The marker being another label postioned using FONTMETRICS * number of characters identified using AT().
Using this method I could make the marker appear as anything I chose, eg red squiggly underline, different coloured text etc.

Fox spell steps through each line looking for spelling errors, counting the characters and building the replacment string as it goes. That way fox spell knows the position of the marker label when it finds a suspect word.
Unlike fox spell, I already have the suspect words from msword so I was not planning to step through the entire string in this manner.





 
In regard to your original code: This is how I would apply ALINES here:

Code:
lcLine = "A wizard iz a magician."

Create cursor cWrongWords (cWord C(50))
Insert into cWrongWords Values ("iz")

lnWcnt=ALINES(laWords,lcLine,16," ",",",".","!","?",";")
* la Words array contains words including their delimitiing characters, whether space or punctuation.
* this is needed to determine the position of a found word as length of all previous words including their delimiter characters!
lcFoundNext=.f.
lnPos = 0
FOR i = 1 TO lnWcnt
	lcCheckWord = ChrTran(laWords[i]," ,.!?;","")
	SELECT cWrongWords 
	Locate FOR ALLTRIM(cWord) == lcCheckWord
	IF Found()
	   EXIT
	EndIf
	lnPos = lnPos + Len(laWords[i]) && add length of word + space or other punctuation
EndFor

?lnPos

Even using GETWORDNUM your code could have used LOCATE and FOUND instead of scanning cWrongWords, there is no need to implement LOCATE this way.

You should really rather concentrate on being a docotor and your hospital should hire a developer or make a contract project by project.

Bye, Olaf.
 
Halfway forget about the last sentence, not only because of my usual typo. It was FoxEgg (John Fox) stating he's a doctor. At least we can assume you're also no developer as your main profession from your own specification of being a technical user .

Bye, Olaf.
 
I would not call myself a developer, but I am a long time programmer and repairer of various legacy hardware requiring logic equations and configuaration programs.
Example:

Code:
WSA=MZA
N1=ALM
N2=FLT
N3=Z19A+Z117A
N4=(Z28A+Z126A+Z164A).N1/126
N5=(Z29A+Z127A+Z165A).N1/126
N6=(Z30A+Z128A+Z166A).N1/126
N16=Z19A+Z117A+Z155A
N17=Z20A+Z118A+Z156A
N18=Z21A+Z119A+Z157A
N19=Z22A+Z120A+Z158A
N20=Z23A+Z121A+Z159A
N21=Z24A+Z122A+Z160A
N22=Z25A+Z123A+Z161A
N23=Z26A+Z124A+Z162A
N24=Z27A+Z125A+Z163A
N25=Z28A+Z126A+Z164A
N26=Z29A+Z127A+Z165A
N27=Z30A+Z128A+Z166A
N28=Z31A+Z129A+Z167A
N29=Z32A+Z130A+Z168A
N30=Z19A+Z117A+Z155A
N31=Z20A+Z118A+Z156A
N32=Z21A+Z119A+Z157A
N33=Z22A+Z120A+Z158A
N34=Z23A+Z121A+Z159A
N35=Z24A+Z122A+Z160A
N36=Z25A+Z123A+Z161A
N37=Z26A+Z124A+Z162A
N38=Z27A+Z125A+Z163A
N39=Z28A+Z126A+Z164A
N40=Z29A+Z127A+Z165A
N41=Z30A+Z128A+Z166A
N42=Z31A+Z129A+Z167A
N43=Z32A+Z130A+Z168A
R15/2=(Z23A+Z121A+Z159A).N1/126
R14/4=(Z24A+Z122A+Z160A).N1/126
R13/3=(Z25A+Z123A+Z161A).N1/126
R10/2=N1/123.N1/124
R9/3=N1/125
R51/3=(Z22A+Z120A+Z158A).N1/126
R54/2=(Z19A+Z117A+Z155A).N1/126
R51/4=N1/123.N1/124
R5/1=N1/125
R11/3=(Z26A+Z124A+Z162A).N1/126
R11/4=(Z27A+Z125A+Z163A).N1/126
R8/2=(Z31A+Z129A+Z167A).N1/126
R8/3=(Z32A+Z130A+Z168A+N2/3).N1/126
R8/4=(Z32A+Z130A+Z168A+N2/3).N1/126
V1=Z32A+Z130A+Z168A+N2/3
R12/3=((Z26A+Z124A+Z162A+V1).N1/126)+N2/4
R12/2=((Z27A+Z125A+Z163A+V1).N1/126)+N2/4
R11/2=((Z28A+Z126A+Z164A+V1).N1/126)+N2/4
R10/3=((Z29A+Z127A+Z165A+V1).N1/126)+N2/4
R9/4=((Z30A+Z128A+Z166A+V1).N1/126)+N2/4
R10/4=N1/123.N1/122
R52/2=N1/125
R52/3=(Z20A+Z118A+Z156A).N1/126

However, the amount of work I have put into this particular (ever growing) software project, I would class myself a developer of this project and associated satellite addon programs.
The project is developed specifically for our business, to align up with our internal structure and requirements. My field experience as a service technician, later experience as an estimator, project manager and now service manager ideally provides me the insights and nuances required to tailor the development of the project to match our needs.
Secondly and most importantly, I really enjoy with a passion the whole process of developing and creating this project.
I am quite simply - hooked on Fox Pro.


 
Oh, and by the way, your ALINES() code snippit is exactly what I need to solve the issue, thanks Olaf
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top