Read a file, write a file, and what changes in a browser tab
Read a whole file into a string, write a string to a file. Everything else is a variation on those two, and both are one call.
One page. Everything here was run.
path.s = "notes.txt";
fwrite(path, "one" + chr(10) + "two" + chr(10) + "three" + chr(10));
printf("exists=%d size=%d\n", fileExists(path), fileSize(path));
whole.s = fread(path);
printf("read %d chars\n", strlen(whole));
exists=1 size=14
read 14 chars
fwrite replaces; fappend adds to the end. Neither needs an open, a handle or a close, because the whole-file case is the one almost every program actually wants and a handle is ceremony you would only be closing again.
There is no line reader, because a file is a string and a string splits.
whole.s = "one" + chr(10) + "two" + chr(10) + "three" + chr(10);
list rows.s;
n.i = split(whole, chr(10), rows);
i.i = 0;
foreach rows {
row.s = listGet(rows, i);
if (row != "") { printf(" %d: %s\n", i + 1, row); }
i = i + 1;
}
1: one
2: two
3: three
A file that ends in a newline splits into one more piece than it has lines, and the last one is empty. That is arithmetic rather than a quirk — three separators make four pieces — and the if above is how every program that reads a text file deals with it.
path.s = "notes.txt";
fappend(path, "four" + chr(10));
printf("after append: %d chars\n", strlen(fread(path)));
fdelete(path);
printf("exists now=%d\n", fileExists(path));
after append: 19 chars
exists now=0
fread on a missing path gives you an empty string, and fileExists is how you tell that from a file that is genuinely empty. Ask before you read when the difference matters:
path.s = "definitely_not_here.txt";
if (fileExists(path)) {
printf("%s\n", fread(path));
} else {
printf("no such file\n");
}
no such file
fwrite("cfg.json", "{ \"port\": 8080, \"host\": \"localhost\" }");
json cfg = parseDoc(fread("cfg.json"));
if (cfg->valid) {
port.i = cfg["port"];
host.s = cfg["host"];
printf("%s:%d\n", host, port);
}
fdelete("cfg.json");
localhost:8080
Read it, parse it, and the values come out as the types you declared. See Read some JSON and use it for the rest.
A CX+AI program compiled to WebAssembly still has all of this, and the files are real but private: they live in the tab's own filesystem, they persist while the page does, and nothing reaches the machine the browser is running on.
That is the honest position rather than a limitation to apologise for — a page that could write to your disk would be a page you should not open. If a program needs to hand a file to the person looking at it, that is a download, which is the page's job rather than the program's.