Copying data to CSV and other text files from FoxPro

Migrating data between different applications can be a time-consuming job. XML promises to make the work easier but there are still a lot of older applications which can't work with XML. Sometimes the only way of exchanging data is to reduce it to a plain text format. Various versions of FoxPro's Copy To command let you generate many different types of text file.

Each version of the Copy To command takes data from the current work area and exports it to a new file. You can use the Fields clause to specify which fields you want and any of the scope clauses to select the records. If you need a complex selection of the records or more sophisticated processing, then use a query to select and process the data into a cursor and then export the data from the cursor.

The Copy To command will export the contents of the current work area regardless of whether this is a table, a cursor or a view. The limitation is that the command is restricted to the current work area so you may have to change work areas before the export and change back afterwards.

Plain CSV file

It's very easy to generate a straightforward comma-separated (csv) file:

Copy To Town.csv Type CSV

This snippet of code will export all the fields of the current table into a text file with commas between each field and with each record on a new line.

What does "delimited" mean?

This is an important question and one which can lead to misunderstandings between programmers who come from different backgrounds. Be very careful when you hear this sort of terminology. You will sometimes hear a CSV file being described as a "comma delimited file". This phrase can be used to mean two different things so make sure that you know what is wanted and ask for a sample of the data before you start work.

Some people would say that these few lines:

1,"Alvechurch"
2,"Bromsgrove"
3,"Catshill"
4,"Dudley"

are typical of the syntax of a comma delimited file. There are two fields in the table and each record has its two values on a separate line.

There is a comma between each data value but you've probably taken the double quotes around the names for granted because that's something of a standard in the PC world. The danger is that we often have to use a csv file when we're sharing data with other computer systems and people working on other operating systems might have a different understanding of the term "comma delimited". They might say that that the output here was separated by commas but that each string value was delimited by double quotes. You're unlikely to have any confusion when you're talking about commas and quotes but if someone asks you to create a file delimited with underscores or pipes then you really do need to see a sample so that you're sure that you understand what is needed.

Note that it's only the string values which have delimiters. Numeric and date values stand alone. Don't put braces around date values because that's FoxPro syntax. Access for example expects a # character either side of a date.

Other delimiters

The extended syntax of the Copy To command starts by following one of these conventions for the meaning of "delimiter" but then contradicts itself. For example, if we specify a dollar sign as the delimiter:

Copy To Town.txt Type Delimited with $

then this command will produce a text file having the following structure:

1,$Alvechurch$
2,$Bromsgrove$
3,$Catshill$
4,$Dudley$

It's obvious that Fox is taking "delimited" to mean the characters marking the strings here. Three further forms of the command take the opposite meaning and take "delimiter" to mean the separator between fields:

Copy To Town.txt Type Delimited with Tab
Copy To Town.txt Type Delimited with Blank
Copy To Town.txt Type Delimited with Character _

These three commands will produce text fields with tabs, spaces and underscores between the fields respectively. All of them retain double quotes as delimiters around the strings:

1   "Alvechurch"
2   "Bromsgrove"
3   "Catshill"
4   "Dudley"
1 "Alvechurch"
2 "Bromsgrove"
3 "Catshill"
4 "Dudley"
1_"Alvechurch"
2_"Bromsgrove"
3_"Catshill"
4_"Dudley"

Finally, we can mix the two terminologies and specify two different "delimiters", one between the fields and another around the strings:

Copy To Town.txt Type Delimited With $ With Character _

This will give us underscores between the fields and dollar symbols around each string:

1_$Alvechurch$
2_$Bromsgrove$
3_$Catshill$
4_$Dudley$

It might look a little confusing but with a bit of experimentation you should find that you can match most formats used to export data as plain text.

Fixed width data

The SDF format does not use delimiters or separators. Instead it generates files where each field is output with trailing spaces at its full width:

Copy To Town.txt Type SDF

If the Town ID fields are four characters long and the Town Names are 15 characters long then this example will generate:

1...Alvechurch.....¶
2...Bromsgrove.....¶
3...Catshill.......¶
4...Dudley.........¶

In this example the spaces have been shown as '.' for clarity and the paragraph markers have been displayed.

Using matched pairs of delimiters

These different forms of the Copy To command let us generate a wide range of text formats but they won't let us generate a text file which has paired separators such as [ and ] or HTML tags like <b> and </b>. If you need to generate a more complicated format such as this example:

1,[Alvechurch]
2,[Bromsgrove]
3,[Catshill]
4,[Dudley]

then you will find Foxpro's TextMerge command useful.

Memo fields in a csv file

None of the forms of the Copy To command will copy memo fields to a csv file. The problem is that the memo field is very likely to contain multiple lines of text and there's no recognised way of storing line feeds and carriage returns in a memo field. One approach might be to store the memo field as HTML. This will fit into the CSV format as plain text and it's easy to convert the carriage returns and line feeds of the memo field into HTML:

Replace All memoField With Strtran(memoField, CHR(13) + CHR(10), "<br />")

This gives us a memo field of plain text but still leaves us with the problem that the the Copy To command will not process a memo field. The solution again is to use the TextMerge function inside a scan loop.

If you do have to create a csv file which includes memo fields then you must talk to the person who'll be receiving and processing the file. They too are going to have to do some preprocessing before they can import your data into their application and they might have restrictions which make the whole process impossible. For example, the FoxPro memo field might be holding more text than their import routine can accept.