Discussion:
VFP 9: Memo Parsing
(too old to reply)
Gene Wirchenko
2010-05-03 17:53:33 UTC
Permalink
Dear Vixens and Reynards:

Over the weekend, I got bit by a bug having to do with memos.

Is there any way to get the contents of the memo and never mind
the set memowidth value?

The remainder of this post is details of implementation (which
might not be relevant):

My app has client invoicing notes. These are all displayed in
the Invoicing Wizard form. Some are so important that my boss wants
them displayed during the actual invoicing in a messagebox. Such note
lines are prefixed with "#" as in:
This is an ordinary note.
#Really important note
Another ordinary note
#YA important note

When generating the important notes messagebox text, I parse the
memo. If a line is long, it may be broken up by memowidth:
#This is an important note that also is rather[break]
lengthy as well.
"[break]" shows where memowidth might break the line. Since "lengthy
as well." is a line and it is not prefixed with "#", it does not get
added to the messagebox text.

I ended up doing
set memowidth to 8192
(8192 being the maximum), but is there a less kludgy way of handling
this?

Sincerely,

Gene Wirchenko
Richard Stecenko
2010-05-03 18:18:36 UTC
Permalink
but is there a less kludgy way of handling this?
For it to be less kludgy, you need an end of important message
indicator as well as a begin of important message indicator.

The problem is that you really don't know how long the messages are:
they could be a few characters on one line, 8192+more characters on
one line, or some number of characters on more then one line.

Setting memowidth works for the 1st case, but not for the other two.

So if your data were organized like this:
This is an ordinary note.
<Really important note>
Another ordinary note
<YA important note>
<the quick brown fox jumped
over the lazy dog.>

then you could develop a less kludgy way of parsing the important
messages.


Richard Stecenko
Interactive Computer Services Inc.
Winnipeg, Canada
204.453.2052
Al Marino
2010-05-03 21:08:42 UTC
Permalink
I heartily agree with the end char
you could assume it ends with a crlf but that will undoubtedly bite you
eventually

you could ask them after a crlf if this was the end and then add the end
char in
or, in your "user preferences" you could give them a choice of "always
assume crlf ends, always ask, never assume"

al
Post by Richard Stecenko
but is there a less kludgy way of handling this?
For it to be less kludgy, you need an end of important message
indicator as well as a begin of important message indicator.
they could be a few characters on one line, 8192+more characters on
one line, or some number of characters on more then one line.
Setting memowidth works for the 1st case, but not for the other two.
This is an ordinary note.
<Really important note>
Another ordinary note
<YA important note>
<the quick brown fox jumped
over the lazy dog.>
then you could develop a less kludgy way of parsing the important
messages.
Richard Stecenko
Interactive Computer Services Inc.
Winnipeg, Canada
204.453.2052
Gene Wirchenko
2010-05-04 00:39:59 UTC
Permalink
On Mon, 03 May 2010 13:18:36 -0500, Richard Stecenko
Post by Richard Stecenko
but is there a less kludgy way of handling this?
For it to be less kludgy, you need an end of important message
indicator as well as a begin of important message indicator.
That is even kludgier, not less!

Remember, an end user will be using this.
Post by Richard Stecenko
they could be a few characters on one line, 8192+more characters on
one line, or some number of characters on more then one line.
Setting memowidth works for the 1st case, but not for the other two.
I know.

It is not going to be an issue here because messagebox() only
handles 1024 characters anyway. (Mind you, it used to be only 512 (or
so).)

It could be an issue if I had complex stuff in that memo, say
source code of some sort.
Post by Richard Stecenko
This is an ordinary note.
<Really important note>
Another ordinary note
<YA important note>
<the quick brown fox jumped
over the lazy dog.>
then you could develop a less kludgy way of parsing the important
messages.
It might be easier to parse, but it is a kludgier design for data
entry. If the end user splits the text onto more than one line, he
has to add to delimiters.

I never have liked how VFP parses memos into lines.

I might as well include my code. A few comments relate to my
app, and you can ignore these. CHRCR is defined by
#define CHRCR chr(13)

***** Start of Included Code *****
* gethashlines
* Get # Lines From Memo
* Last Modification: 2010-05-02
*
* Returns the # lines of the memo in one string

procedure gethashlines
lparameters;
thememo && M: the memo to split up
&& Assumed to not have any physical lines of
length >8192

local svmemowid, maxlinelen, memolines
svmemowid=transform(set("memowidth"))
set memowidth to 8192
maxlinelen=70 && N: arbitrary, but set to width of note
&& The messagebox note may split
differently from
&& the cftiwiz:: display as leading blanks
in
&& subsequent parts of a #-line are
suppressed.
memolines=""

if !empty(thememo)
local cline, first, i, workline
cline=memlines(thememo)
_mline=0
first=.t.
for i=1 to cline
workline=alltrim(mline(thememo,1,_mline))
if left(workline,1)="#"
local wllen, startptr, endptr
wllen=len(workline)
startptr=2 && With 2, the "#" will be omitted.
do while startptr<=wllen
endptr=startptr+maxlinelen
if endptr>wllen
linelen=endptr-startptr
else
local clipptr, looping
clipptr=endptr && Start at one past end to
catch the
&& case of the maxlinelen'th
char not
&& being a space and next one
being a
&& space.
looping=.t.
do while clipptr>=startptr and looping
if substr(workline,clipptr,1)=" "
looping=.f.
linelen=clipptr-startptr
else
clipptr=clipptr-1
endif
enddo
if looping && no blanks at all in subline
linelen=maxlinelen
endif
endif

if !first
memolines=memolines+CHRCR
endif
first=.f.
memolines=memolines+trim(substr(workline,startptr,linelen))

startptr=startptr+linelen

* Skip leading blanks.
do while startptr<=wllen and;
substr(workline,startptr,1)=" "
startptr=startptr+1
enddo

enddo

endif
endfor
endif

set memowidth to &svmemowid

return memolines

endproc
***** End of Included Code *****

Sincerely,

Gene Wirchenko
JayB
2010-05-04 03:19:18 UTC
Permalink
its not clear to me what your function is really returning.
can you provide an example of a long memo field that you are trying to
parse. and what your return results should be.

i store lots of things in long memo fields and never run into the 8192
limit.
because i dont use memlines, or set memowidth ever.

you can use the alines() function with a parse character to split your
lines if that is what you are trying to do.
Post by Gene Wirchenko
On Mon, 03 May 2010 13:18:36 -0500, Richard Stecenko
Post by Richard Stecenko
but is there a less kludgy way of handling this?
For it to be less kludgy, you need an end of important message
indicator as well as a begin of important message indicator.
That is even kludgier, not less!
Remember, an end user will be using this.
Post by Richard Stecenko
they could be a few characters on one line, 8192+more characters on
one line, or some number of characters on more then one line.
Setting memowidth works for the 1st case, but not for the other two.
I know.
It is not going to be an issue here because messagebox() only
handles 1024 characters anyway. (Mind you, it used to be only 512 (or
so).)
It could be an issue if I had complex stuff in that memo, say
source code of some sort.
Post by Richard Stecenko
This is an ordinary note.
<Really important note>
Another ordinary note
<YA important note>
<the quick brown fox jumped
over the lazy dog.>
then you could develop a less kludgy way of parsing the important
messages.
It might be easier to parse, but it is a kludgier design for data
entry. If the end user splits the text onto more than one line, he
has to add to delimiters.
I never have liked how VFP parses memos into lines.
I might as well include my code. A few comments relate to my
app, and you can ignore these. CHRCR is defined by
#define CHRCR chr(13)
***** Start of Included Code *****
* gethashlines
* Get # Lines From Memo
* Last Modification: 2010-05-02
*
* Returns the # lines of the memo in one string
procedure gethashlines
lparameters;
thememo && M: the memo to split up
&& Assumed to not have any physical lines of
length >8192
local svmemowid, maxlinelen, memolines
svmemowid=transform(set("memowidth"))
set memowidth to 8192
maxlinelen=70 && N: arbitrary, but set to width of note
&& The messagebox note may split
differently from
&& the cftiwiz:: display as leading blanks
in
&& subsequent parts of a #-line are
suppressed.
memolines=""
if !empty(thememo)
local cline, first, i, workline
cline=memlines(thememo)
_mline=0
first=.t.
for i=1 to cline
workline=alltrim(mline(thememo,1,_mline))
if left(workline,1)="#"
local wllen, startptr, endptr
wllen=len(workline)
startptr=2 && With 2, the "#" will be omitted.
do while startptr<=wllen
endptr=startptr+maxlinelen
if endptr>wllen
linelen=endptr-startptr
else
local clipptr, looping
clipptr=endptr && Start at one past end to
catch the
&& case of the maxlinelen'th
char not
&& being a space and next one
being a
&& space.
looping=.t.
do while clipptr>=startptr and looping
if substr(workline,clipptr,1)=" "
looping=.f.
linelen=clipptr-startptr
else
clipptr=clipptr-1
endif
enddo
if looping && no blanks at all in subline
linelen=maxlinelen
endif
endif
if !first
memolines=memolines+CHRCR
endif
first=.f.
memolines=memolines+trim(substr(workline,startptr,linelen))
startptr=startptr+linelen
* Skip leading blanks.
do while startptr<=wllen and;
substr(workline,startptr,1)=" "
startptr=startptr+1
enddo
enddo
endif
endfor
endif
set memowidth to &svmemowid
return memolines
endproc
***** End of Included Code *****
Sincerely,
Gene Wirchenko
Jürgen Wondzinski
2010-05-04 10:03:16 UTC
Permalink
Hi Gene,

why not just parse like that:

FOR i = 1 TO OCCURS("#", YourMeno)
? STREXTRACT(YourMemo, "#", CHR(13), i)
ENDFOR

Should give you all lines starting with a HashSign.
--
wOOdy
Visual FoxPro Evangelist
Microsoft "Most Valuable Professional" 1996 to 2009



"*´¨)
¸.·´¸.·*´¨) ¸.·*¨)
(¸.·´. (¸.·` *
..·`.Visual FoxPro: It's magic !
(¸.·``··*
JayB
2010-05-04 13:50:33 UTC
Permalink
exactly... that's what i was getting at, but not knowing what he is
trying to accomplish.
Post by Jürgen Wondzinski
Hi Gene,
FOR i = 1 TO OCCURS("#", YourMeno)
? STREXTRACT(YourMemo, "#", CHR(13), i)
ENDFOR
Should give you all lines starting with a HashSign.
Gene Wirchenko
2010-05-04 19:11:17 UTC
Permalink
On Tue, 4 May 2010 12:03:16 +0200, Jürgen Wondzinski
Post by Jürgen Wondzinski
Hi Gene,
FOR i = 1 TO OCCURS("#", YourMeno)
? STREXTRACT(YourMemo, "#", CHR(13), i)
ENDFOR
Should give you all lines starting with a HashSign.
Not quite.

It will give me something for all lines (not including the final
line of the memo) that have "#" *ANYWHERE* in them. It will only give
the portion of the line after the "#". A line with more than one "#"
will give trouble (repeating the last part of the line). The last
line of a memo does not have a CR, so that line will be ignored.

I did not know about strextract(). Thank you for the pointer. I
am pleased that it ignores the set memowidth setting.

My code puts a CR on each end of the memo and scans each line
(delimited on each end with CR). Again, CHRCR is
#define CHRCR chr(13)

***** Start of Included Code *****
* gethashlines
* Get # Lines From Memo
* Last Modification: 2010-05-04
*
* Returns the # lines of the memo in one string with consecutive
lines
* delimited with CR

procedure gethashlines
lparameters;
thememo && M: the memo to split up

local memocred, memolines, i, oneline

memocred=CHRCR+thememo+CHRCR
memolines=""

for i=1 to occurs(CHRCR,memocred)-1
oneline=strextract(memocred,CHRCR,CHRCR,i)
if left(oneline,1)="#"
if !empty(memolines)
memolines=memolines+CHRCR
endif
memolines=memolines+substr(oneline,2)
endif
endfor

return memolines

endproc
***** End of Included Code *****

Sincerely,

Gene Wirchenko
Bernhard Sander
2010-05-05 09:46:15 UTC
Permalink
Hi Gene,

don't use memlines(), mline() and _mline, they respect SET MEMOWIDTH.
Instead use ALines() to cut the memo field in an array of lines.
Of course, this makes a copy of the content of the memo field and is not the
"streaming" operation as with your loop, but ALines does not respect SET MEMOWIDTH.

Regards
Bernhard Sander
Gene Wirchenko
2010-05-05 17:50:50 UTC
Permalink
Post by Bernhard Sander
don't use memlines(), mline() and _mline, they respect SET MEMOWIDTH.
Instead use ALines() to cut the memo field in an array of lines.
Of course, this makes a copy of the content of the memo field and is not the
"streaming" operation as with your loop, but ALines does not respect SET MEMOWIDTH.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Thank you for the pointer.

The situation with memos is better than I thought. Streaming
would be best, but most memo values are short anyway in my app. (If
they were longer, they would probably be worth breaking up into other
columns.)

Sincerely,

Gene Wirchenko

Loading...