MemLines(), MLine(), _MLine
The functions MEMLINES() and MLINE() and the system variable _MLINE let you pull apart a memo field or multi-line character string into its component lines. MEMLINES() tells you how many lines the field contains while MLINE() returns a specified line. _MLINE keeps track of how far into the field you've gone looking for lines.
Usage |
nNumberOfLines = MEMLINES(cString ) cResult = MLINE(cString, nLine [ , nStartPos ] ) _MLINE = nStartPos nStartPos = _MLINE |
Parameter |
Value |
Meaning |
cString |
Memo or Character |
The string to be broken into lines. |
nLine |
Numeric |
The line to return with MLINE(). |
nStartPos |
Numeric |
The offset from the beginning of the string to start looking for lines. |
nNumberOfLines |
Numeric |
The number of lines in cString (based on SET("MEMOWIDTH")). |
cResult |
Character |
The nLine-th line of cString, starting from nStartPos, based on SET("MEMOWIDTH"). This string is trimmed and doesn't include a carriage return or line feed at the end. |
_MLINE lets you return successive lines without having to count as you go. Each call to MLINE() assigns _MLINE the place to start looking for the next line after the one returned. That is, if MLINE() returns a 23-character line from the beginning of a string, _MLINE gets set to 24 automatically. This means you can pass _MLINE for nStartPos and leave nLine set at 1 to return successive lines of a memo field or string. To do so, set _MLINE=0 before applying MLINE() the first time. |
FOR I = 1 TO MEMLINES() ? MLINE(myString,i) NEXTThis second snippet doesn't appear much different:
_MLINE = 0 FOR I = 1 TO MEMLINES() ? MLINE(myString,1,_MLINE) NEXTHowever, the second code fragment can run circles around the first! Why? The reason is that, in the first example, Visual FoxPro has to read the file and break it into lines, one line at a time, until it gets to the line number you've specified. In the second example, you've already supplied the offset where the function must start counting, and told it just to return the first line—isn't that a lot easier?One subtlety here is that _MLINE is an offset, not a position. That is, it's the number of characters to move from the first character to find the starting position. Huh? With an offset, you start at a given position and move the specified number of characters to the right. The one you land on is the place to start. This means that _MLINE is one less than the position of the first character to be considered.If you think of _MLINE as a position, you'll run into trouble in one situation. The interaction between MLINE() and _MLINE is designed to remove the blank space between words when a line break occurs. That is, the space between two words doesn't become part of either line when that word break is used as a line break. So, _MLINE appears to point at the space.However, if there are no spaces and MLINE() simply takes the maximum number of characters, _MLINE points at the last character in the first line. The second line properly starts with the next character. What all this means is that you can't count on _MLINE being the number of characters used so far.Many folks miss the fact that this group of goodies can be applied to character strings just as well as to memo fields. They're very handy for dividing a message to be displayed into lines of the right length.The ALines() function added in VFP 6 lets you do a lot of the same things you can do with MLINE() and _MLINE, but doesn't trim the result or rely on the setting of MEMOWIDTH, which may have advantages for many uses. ALines() is generally far faster than MLINE(), so use it unless you need MEMOWIDTH respected.These functions should also be considered as replacements for low-level functions on text files. Low-level functions typically read a file one character or line at a time, process it, and then go back for more. If, instead of doing this, you plunk the whole file into a memory variable or memo field, you can then run these functions on the result. Because Visual FoxPro will buffer as much of the data in memory as it can manage, the difference in performance can be impressive.
Example |
* Take a message and divide it into appropriate length lines nOldMemo = SET( "MEMOWIDTH" ) SET MEMOWIDTH TO 30 nNumLines = MEMLINES( cMessage ) DIMENSION aMessage[ nNumLines ] _MLINE = 0 FOR nCnt = 1 TO nNumLines aMessage[nCnt] = MLINE(cMessage, 1, _MLINE) ENDFOR SET MEMOWIDTH TO nOldMemo |
See Also |
ALines(), AtCLine(), AtLine(), Low-Level File Functions, RAtLine(), Set MemoWidth |