Plateforme Level Extreme
Abonnement
Profil corporatif
Produits & Services
Support
Légal
English
Articles
Recherche: 

Text Manipulation
Hilmar Zonneveld, August 1, 2003
In this article, I will show several ways to manipulate text-files. Knowledge of these methods is often important to import and export specific formats. Some of the techniques can also be used to work with files of any content; however, this article will concentrate on text-files. When ...
In this article, I will show several ways to manipulate text-files. Knowledge of these methods is often important to import and export specific formats.

Some of the techniques can also be used to work with files of any content; however, this article will concentrate on text-files.

When do you need to manipulate text-file?

There may be several cases where you need to create or read text-files (or binary files, in some cases), for instance:

  • You need to import data from another program into your application.
  • You need to export data from your application, to a very specific format, required by another application.
  • You want to generate HTML output automatically, for instance, as an additional reporting option, for sending as e-mail attachments, or for publication to a Web site.
  • You want to print to a dot-matrix printer. This makes it convenient to print in text-mode, since on some printers, this can print 2-3 times faster, or even more, than if you use the standard reports.

Note that for interfacing with other programs, importing and exporting to text-files or binary files is not the only option. Other options, for instance, XML files (which are actually special text-files), ODBC and Automation, are beyond the scope of this article.

Standard import and export commands

The standard commands for importing and exporting text-files are APPEND FROM (for import) and COPY TO (for export). They are relatively easy to use, in that they don't require you to loop through the records - these commands can can process an entire table at once. The fact that you use a single command also makes these commands very fast.

Two common text-formats are supported. With a comma-delimited text-file, each field is separated from the next with a comma (other characters are supported, too). With a fixed-width text-file, each field is identified solely by its position. For instance, the first 10 bytes in each record might correspond to the first field, the next 3 bytes to the second field, etc. In both cases, records are separated by the standard new-line symbol (carriage-return and line-feed, that is, chr(13) + chr(10)). The corresponding keywords are DELIMITED and SDF.

For exporting to a text-file, just select a table or prepare a cursor, and use a command similar to:

copy to MyText.txt type SDF
* (or: type delimited)

For importing a text-file into a table, you need to prepare a table or cursor with the correct structure first.

These commands are very appropriate for a quick import or export, but if your text-data has (or requires) a very specific format, that can't be handled by these two standard text-formats, you may need some of the additional commands, to which I dedicate the remainder of this article.

Standard report in text-mode

A standard VFP report can be output as a text-file:

report form TestReport to file output.txt ascii

The variables _asciicols and _asciirows specify how many columns and lines you want on a page. Specifically, _asciicols will alter the horizontal spacing between elements on an existing report.

However, I found it very hard to have an exact control over the placement of individual objects; basically, this requires trial-and-error. Also, if you later happen to need a wider report than original planned (increasing _asciicols), you have to redo the element spacing all over again. Therefore, I don't recommend this option.

An alternative that makes it easier to control the placement of objects is to create and maintain the report with FoxPro/DOS, and then run it in Visual FoxPro, without converting it.

FileToStr(), StrToFile(), alines() and mline()

In VFP 6 or later, you can use FileToStr() and StrToFile() to copy quickly from a file on disk to a memory variable, or the other way round.

The following sample creates a text-file with ten lines:

erase output.txt
for i = 1 to 10
  StrToFile("Line " + trans(i) + chr(13) + chr(10), "output.txt", .T.)
next

Note that if the third parameter is .T., the output will be appended to the file, instead of overwriting the entire file.

For importing data, the alines() function is especially easy to use, since it converts each line into an array element.

The following example will show the contents of the autoexec.bat file, adding line numbers to the left:

for i = 1 to alines(laMyText, FileToStr("c:\autoexec.bat"))
  ? transform(i, "###"), laMyText(i)
next

Manipulation with alines() is very fast, too. However, unfortunately, the VFP limitation of 65,000 elements per array translate to a maximum of 65,000 lines of text per file.

If you currently use more lines, or want to plan for future growth, you can use the mline() function instead. The help file states that it is designed for memo fields, but it works very well for long character expressions in memory, too.

I will now repeat the previous example, using the mline() function. Since the idea is to use this sort of code on possibly large files, the sample is optimized for speed in two ways: 1) I used the _mline variable, as explained in the VFP help. 2) I abstained from counting the total number of lines beforehand, with memlines(). The first point is especially important. For instance, if you use mline() to return line #1000 directly, VFP must first search for end-of-line characters for the first 999 lines, in order to find the 1000th. line.

set memowidth to 200
lcMyText = FileToStr("c:\autoexec.bat")
_mline = 0
i = 0
FileSize = len(lcMyText)
do while _mline < FileSize - 1
  i = i + 1
  ? transform(i, "###"), mline(lcMyText, 1, _mline)
enddo

This will solve the alines() limitation of maximum 65,000 array elements. However, reading a large file into RAM in one single piece can be very slow, if the file is larger than the free RAM (this will usually work, but the use of virtual memory can slow things down considerably). If this is the case, I would recommend using low-level file-functions instead.

Low-level file-functions

Low-level file-functions (LLFF) give you the ultimate control over each and every byte you import or export. First, as an example, let's repeat the example of showing the autoexec.bat file, this time with LLFF:

lnFile = fopen("c:\autoexec.bat")
if lnFile < 0
  MessageBox("Error opening file.")
else
  i = 0
  do while not feof(lnFile)
    i = i + 1
    ? transform(i, "###"), fgets(lnFile)
  enddo
  fclose(lnFile)
endif

Since LLFF are a little more involved than the previously explained methods, I will try to explain the individual steps.

First, an existing file is opened with fopen(), or (for the export case) a new file is created with fcreate().

These functions return a "file handle", a number which has to be stored into a variable, in order to access the same file later in the program. If the file can be opened or created successfully, a positive number is returned. A -1 indicates some problem. (Note that I didn't include error handling in the other examples - I had assumed the file existed.)

An entire line can be read from the file thus opened, with the fgets() function. For output, an entire line can be written with fputs(). If you need to work with individual bytes, you can use fread() and fwrite() instead.

For importing data, you will usually want to continue reading data until you reach the end of the file, which you detect with feof(). After you do all required processing, whether you opened an existing file or created a new file, it has to be explicitly closed, with fclose().

For completeness sake, here is a sample code for the export case. This program will create a short text-file, using LLFF:

lnFile = fcreate("output.txt")
if lnFile < 0
  MessageBox("Error creating file.")
else
  fputs(lnFile, "Line 1")
  fputs(lnFile, "Line 2")
  fclose(lnFile)
endif

Output with ? and ??

The ? and ?? commands (as well as the commands in the next two sections) are for the export case only, that is, to create text-files.

Both the ? and the ?? commands will output the expressions specified. If you specify more than one expression, separated with commas, they are automatically separated by a space. However, separate ? commands will not add this space.

?? will add a new line to the output, ? will not. Note that unlike similar commands in other languages, the line is added before the rest of the output specified in the command. By default, the output goes to the screen.

Now, let's see a short sample, how to produce a text-file with these commands. For illustration purposes, I separated the code to produce the second line into two separate commands.

* Setup code
set console off
set alternate to output.txt
set alternate on

* Produce the output
?? "This is line 1"
? "This", "is"
?? " line", "2"

* Cleanup code
set alternate off
set alternate to
set console on

Once you SET ALTERNATE to a file, output of several other commands, like LIST, LIST STRUCTURE and DIR, will also go to your text-file.

Output with @...SAY

The @...SAY command, which also prints to the screen by default, is another option to create text-files.

This command lets you output to specific coordinates of your text-file immediately. In other words, you can specify a line or column number directly, without explicitly outputting empty lines or columns. However, output has to be produced in ascending order. VFP treats the output as if it were a printer, which means that going back to a previous line will insert a page-break.

set device to file output.txt
@ 0, 10 say "This is the title"
@ 3, 0 say "Some text"
@ 2, 0 say "This would go to the next page"
set device to screen

Visual FoxPro keeps track of row and column positions; these can be accessed with the row() and col() functions; or you can just print relatively to the current position with the special $ operator instead of the number coordinates:

set device to file output.txt
@ 1, 0 say "Hello"
@ $, $+2 say "World"
@ $+3, 0 say "Some more text"
set device to screen

Unfortunately, the help topic for this versatile command only says it is "included for backward compatibility". For more details, you will therefore have to get the help file for FoxPro 2.x.

Outputting with TEXTMERGE

Another alternative for outputting text files is to use the TEXT and SET TEXTMERGE commands. You can either output lines of text with \ and \\, or, to avoid the repeated use of these symbols on several consecutive lines, between TEXT and ENDTEXT. The following sample illustrates how this is done:

set textmerge on to output.txt noshow

\\Line 1
\Line 2

text
Line 3

Line 5
Today is <<cdow(date())>>, <<date()>>.
Your hard disk has <<transform(diskspace(), "###,###,###,###")>> bytes free.
endtext
set textmerge to

Note that when you combine constant text with variables or expressions, your code is much more readable with these commands, than with the other alternatives shown.

Handling binary files

All the previous examples are for text-files. You can use some of the options for binary files, that is, files in any format. For instance, ? and ?? can output any expression, not just plain text; StrToFile() and FileToStr() can work with both text and binary data, and the low-level file functions can work both on entire lines of text, and on individual bytes.

The real problem with binary files, of course, is that a binary format may be quite complicated, and it may be difficult to get documentation on the details of how some file types are stored.

Printing

If you want to print your text-file in text-mode, you can use the "???" command to send printer codes, followed by your text-file, directly to the printer. No interpretation is done by the Windows printing system - even a page-break has to be added explicitly (check your printer manual - for standard Epson dot-matrix printers, it is chr(12)).

Also, Universal Thread download #9957, by the late Ed Rauh, is a class specifically designed to bypass the Windows printing system, and print directly to the printer.

I have created a class to take care of many of the repetitive details of creating text-mode reports (like programming the main loop and taking care of break expressions, among others). It is available as Universal Thread download #9991. Other text-report downloads are available as well, but I haven't tested them thoroughly.

Conclusion

Visual FoxPro offers you several alternatives to access text-files. It is very likely that sooner or later, you will need some of them, for the purposes outlined at the beginning of this article.

Hilmar Zonneveld, Independent Consultant
Hilmar Zonneveld works in programming since 1986, using dBASE, FoxPro and Visual FoxPro. He is available as an independent consultant. He currently works as a programmer at Bata Shoe Organization; also as an instructor at Cisco Networking Academy. You can contact him through the Universal Thread, or, via e-mail, at hilmarz@yahoo.com. Personal Web page (mainly in Spanish): www.geocities.com/hilmarz.
More articles from this author
Hilmar Zonneveld, May 1, 2003
An audit-trail is a record of who did what changes, and when. In Visual FoxPro, this can easily be accomplished through triggers. I hinted at the possibility of doing an audit-trail, in my article on triggers - now, as a reaction to questions in the Universal Thread, I want to present a sample...
Hilmar Zonneveld, December 6, 2001
(The latest update contains minor edits only.) Five easy and fun ways to get yourself into trouble with inheritance. A frequent source of problems in OOP is called "breaking inheritance". This document briefly describes what inheritance is, how it applies to properties and methods, and how it ...
Hilmar Zonneveld, July 1, 2002
Introduction Buffering is a feature in Visual FoxPro that allows us to give the user "undo" and "save" capabilities. In the old FoxPro 2.x days, programmers either didn't provide this capability, or edited memory variables, and copied information between these variables and the table fiel...
Hilmar Zonneveld, October 6, 2005
Due to a recent Windows security fix, users can no longer access a CHM file on a server. The table of contents appears, but the individual pages are replaced by error messages. Access to CHM files in specific folders can be explicitly allowed through special registry settings.
Hilmar Zonneveld, July 20, 2001
(The last update contains minor edits only.) The idea is to have several controls on a form controlled with an array. Thus, you can quickly go through all the controls on the form, managing the array. The sample code included will help you get started quickly. You can easily adapt it to manage...
Hilmar Zonneveld, September 1, 2002
With Automation, you can control all aspects of Excel, Word, or other programs that provide this feature, from Visual FoxPro. In this article, I will concentrate on Excel. Its purpose is to provide a starting point, especially for people new to automation. Introduction With automation, you bas...
Hilmar Zonneveld, March 1, 2003
Introduction One common task in programming is to keep track of what problems are pending. For this purpose, I use a "hierarchical to-do list": a list of items, each of which can have sub-items. All you need is Microsoft Word. Alternatives are available as freeware or shareware, but in t...
Hilmar Zonneveld, October 7, 2005
This is a step-by-step tutorial to show inheritance, specifically in Visual FoxPro forms, as a guidance for people who are not familiar with inheritance in general, or who don’t know how to implement it in Visual FoxPro. The basic idea of inheritance is that all your forms, or several of your for...
Hilmar Zonneveld, May 30, 2004
The code shows how to quickly obtain the greatest common factor, and the least common multiple. Both functions are used when manipulating fractions, among others. Several methods are possible; the method usually taught in school involves prime numbers, but this code will execute much faster (and it ...
Hilmar Zonneveld, November 1, 2006
A standard requirement in a production system, or in systems for cost calculation, is to add up all the raw materials for a number of finished articles, to get the total cost, or simply to purchase the materials. In this article, Hilmar outlines how to do this with multiple levels of intermediate ar...
Hilmar Zonneveld, August 1, 2002
Overview The purpose of this article is to give an overview of normalization. Basically, normalization refers to having an efficient table structure. I will not discuss the famous "first to fifth normal forms" - if you want that information, enough texts exist about it in other places (search sit...
Hilmar Zonneveld, November 8, 2001
The following function will open any document, with its default association (the same application that will be called when you double-click on the file, in Windows Explorer). Use it to open a text-file, a Word or Excel document, an image, etc., with an external application.
Hilmar Zonneveld, May 1, 2002
Introduction This document explains the meaning of primary key, foreign key and candidate index in Visual FoxPro. A discussion of natural and surrogate keys (keys visible, or not visible, to the end-user) is included, including the advantages of each approach, as well as different methods for o...
Hilmar Zonneveld, January 1, 2003
Continuing my series of introductory articles, this article presents an introduction of a simple yet powerful programming concept: recursion. Introduction "To understand recursion, you must first understand recursion." "To make yogurt, you need milk and yogurt." If you are not accustomed...
Hilmar Zonneveld, December 1, 2002
Introduction This article presents an introduction to coding shortcuts in Visual FoxPro - when to use them, and when not to. Notes on coding in general This article is about coding shortcuts; however, I should first emphasize that making the code as small as possible is usually not the number...
Hilmar Zonneveld, July 20, 2001
Rushmore Optimization can help make queries much faster. However, "Full Rushmore Optimization" is not always a desirable goal. "Partial Optimization" is sometimes much faster. It is often believed that to speed things up, you need to have as many indices as possible. This article explains that so...
Hilmar Zonneveld, June 7, 2002
If you need to check elapsed time with seconds() or a datetime value, this function allows you to display the elapsed time in a human-readable format, that is, hours:minutes:seconds, instead of the total number of seconds. Just pass a number of seconds as a parameter.
Hilmar Zonneveld, April 1, 2002
SQL is a standard language used to manipulate databases. Several of the SQL commands are integrated into the Visual FoxPro language. Select This is a very flexible command, used to select data from a table, or from several tables. This command has options to get totals from several record...
Hilmar Zonneveld, June 1, 2002
The purpose of this article is to show how to use some aspects provided by the Visual FoxPro database engine, to control our data. Indices Perhaps most readers already know indices; anyway, I find it convenient to include a brief summary of the topic, since this is a requisite to understan...
Hilmar Zonneveld, November 1, 2002
A help file can be used either for interactive help, or as an online manual. In this article, I will give an overview over creating help files in the new help format (CHM), for Visual FoxPro 6 and later. This article is introductory and assumes no prior knowledge of the Help Compiler, or of HTML cod...
Hilmar Zonneveld, February 1, 2003
Introduction Any real-world application will sooner or later misbehave. It is important to be able to find those problems. Visual FoxPro's built-in debugger can help a lot to find out why your program doesn't work as you thought it would. Most of the material in this article applies to Visual...
Hilmar Zonneveld, May 1, 2006
This article is an introduction to VisioModeler. This is a free CASE tool, that can help you design your database, in the process sharing the information with the client in a visual, easy-to-understand, format.