File I/O
Console I/O
0 "this goes to console"
1 "this goes to stdout"
2 "this goes to stderr"
If you use negative handlers, it means asynchronous write. This will add new line characters to new lines.
To write a file to console I/O, do something like
\1 txt_for_stdout.txt
File paths
File paths in KDB start with
`:.
You can use
hsym to turn a symbol/string into a file path, like this:
hsym "my/file/path.
You can use
sv to construct file paths from a list of folders, like this:
` sv `:[list of folders].
To list all files under a path, do this
key `:.
File I/O
KDB files
// write to KDB file
`:filepath set tbl
// read from KDB file
get `:filepath
If this KDB file is a flat table, you will be able to edit it directly on disk using qsql.
For example, something like
select from `:filepath where sym=`AAPL
TXT files
// write to txt
fileHandle: hopen `:tbl.txt
fileHandle "writing some text without starting a new line"
neg[fileHandle] "starting a new line"
hclose fileHandle
// or use 0:
`:tbl.txt 0:("hello";"goodbye")
// read from txt
read0 `:tbl.txt
CSV files
// write to csv
save `:tbl.csv
// read from csv
// (type; delimiter) 0: `:tbl.csv
("SJF"; enlist ",") 0: `:tbl.csv
Type is a string that specifies the data type of each column, SJF here means symbol, long, float.
Type must be provided.
JSON files
// write to json is the same as csv
save `:tbl.json
// read from json
load `:tbl.json
Summary
In KDB, there are actually several pairs of I/O.
- High level abstraction:
save and load
This pair can deal with almost all file formats, but they are less flexible. For example, you can't
change the file name when you save/load. Neither can you manage compression level.
-
Deal with tables:
set, get
This pair is used to store and retrieve tables. You have the freedom to choose file name and compression
level.
-
Deal with text files and csvs:
0:, read0
This pair is used to read and write text files or csvs. They don't require the file to be structured
like a table.
-
Deal with bytes:
1:, read1
This pair is used to read and write bytes.
q)`:test.txt 0:("hello";"goodbye") // write some text to a file
q)read1`:test.txt // read in as bytes
0x68656c6c6f0a676f6f646279650a
q)"c"$read1`:test.txt // convert from bytes to char
"hello\ngoodbye\n"
-
Pipes:
hopen, hclose
In fact, the higher level functions are just wrappers around the lower level functions. For example,
save is just a wrapper around
set. And as you see in the example above,
read0 is just a wrapper around
read1 with some extra parsing.
IPC
Basic client and server
Sync flush: the server will receive stuff you send one by one
// on the server side
q) h:hopen `::portnumber
// on the client side
q) h:hopen `:localhost:portnumber
q) result:h("select from tablename where condition") // query some data
q) hclose h
Async flush: the server will receive all the stuff you send
q) neg[h][] // this blocks the client until it receives something from the server
// alternatively
q) neg[h](::) // doesn't block client; client can send h "" to ask the server for confirmation
Advanced IPC using .z namespace
On the client side, you can't do much except those mentioned above.
But on the server side, .z namespace can help you achieve a lot more functionalities.
talk to client process
You can check the ip address and the user of the calling process using
.z.a and
.z.u.
To talk to the client, you can open a handle to it using
.z.w, and with this handle, you can send
messages back to the calling process, like how you send messages the other way around using
h.
These three functions are defined by KDB, you don't need to define their behaviors.
In the following sections, the functions act as event handlers. You need to define their behaviors when certain
events happen.
You can reset the behaviors of those event handlers using
\x such as
\x .z.pw
Set up permissions
.z.pw is triggered when a client process tries to establish a new connection with the server.
A common pattern to use permissions is:
// on the server side, define .z.pw
q) .z.pw:{[myun; mypw] 1b}
// on the client side, connect with username and password
q) h:hopen `:host:port:myun:mypw
Or you can add username and password pairs to a table.
Monitor current connections
.z.po is triggered when a new connection is open.
.z.pc is triggered when a connection is closed.
Both of them by default take one argument, and you can define them like so:
q) .z.po:{0N!"Connection open on ", string[x]}
q) .z.pc:{0N!"Connection closed on ", string[x]}
Again, you can define the functions to add/remove an entry to a table of active connections.
Message handlers
.z.pg is triggered when server receives a synchronous message from the client.
.z.ps is triggered when server receives an asynchronous message from the client.
.z.ph is the http alternative for
.z.pg and
.z.ps.
One usecase is to decide who has permission to run which function.
q) .z.pg{[query]
class: .perm.users[.z.u][`class];
$[class~`superuser; value query; \"Permission denied\"]
}