One way, if the format you want is csv, is automatically invoked when using the save function if the name of the destination file ends in .csv:

q)t: ([] x: `a`b; y: 1 2)
q)save `:t.csv
q)\cat t.csv
“x,y”
“a,1”
“b,2”
q)

For other delimiters, you must first format the text using the overload of the 0: function whose first parameter is a character:

q)t: ([] x: `a`b; y: 1 2)
q)”|” 0: t
“x|y”
“a|1”
“b|2”
q)

Notice that, by default, q saves the table column names as a header in the first line of the file. If you want to get rid of the header line, use _ (drop):

q)t: ([] x: `a`b; y: 1 2)
q)1 _ “\t” 0: t
“a\t1”
“b\t2”
q)

Once you have your data as a list of strings complete with delimiters, you use another overload of the 0: function to save the data to disk; this time, the first parameter is the file handle (name) of the destination file:

q)t: ([] x: `a`b; y: 1 2)
q)`:filename.psv 0: 1 _ “|” 0: t
`:filename.psv
q)\cat filename.psv
“a|1”
“b|2”
q)

Since we can’t seem to keep all of the overloads for 0: straight (there are more we didn’t cover here), we like to wrap the above idiom in a function with a descriptive name. For example,

SaveTableDataAsText: {[path; table; delimiter] hsym[path] 0: 1 _ delimiter 0: table}

See also: .h.cd