Is it a binary file? Is it a text file? It's a Blob
Let's say we want toget the contents of a fileas a value, using a simple function, without using a port or looping. Kawa has a function to do that:
(path-data path)The path can be a
Path
object,
or anything that can be converted to a Path
, including
a filename string or a URL.
You can also use the following syntactic sugar, which is an example of SRFI-108 named quasi-literals:
&<{pname}This syntax is meant to suggest the shell input redirection operator
<pname
.
The meaning of &<{pname}
is the same as
(path-data &{pname})
,
where &{pname}
is a
SRFI-109 string quasi-literal. (This is almost the same as (path-data "pname")
using a traditional string literal, except for the rules for quoting and escaping.)
What kind of object is returned by &<{pname}
?
And what is printed when you type that at the REPL?
Fundamentally, in modern computers the contents of a file is
a sequence of uninterpreted bytes.
Most commonly, these bytes represent text in a locale-dependent encoding,
but we don't always know this. Sometimes they're images, or videos,
or word-processor documents. It's like writing assembly code:
you have to know the types of your values.
At best we can guess at the type of a file
based on its name or extension or looking for magic numbers.
So unless we have more information, we'll say that
path-data
returns a
blob,
and we'll implementing it using the gnu.lists.Blob
type.
$ cat README Check doc directory. $ kawa #|kawa:1|# (define readme &<{README}) #|kawa:2|# readme:class class gnu.lists.BlobYou can explicitly coerce a Blob to a string or to a bytevector:
#|kawa:3|# (write (->string readme)) "Check doc directory.\n" #|kawa:4|# (write (->bytevector readme)) #u8(67 104 101 99 107 32 100 111 99 32 100 105 114 101 99 116 111 114 121 46 10) #|kawa:5|# (->bytevector readme):class class gnu.lists.U8Vector
The output of a command (which we'll discuss in the next article): is also a blob. For almost all programs, standard output is printable text, because if you try to run a program without re-direction, and it spews out binary data, it may mess up your terminal, which is annoying. Which suggests an answer to what happens when you get a blob result in the REPL: The REPL should try to print out the contents as text, converting the bytes of the blob to a string using a default encoding:
#|kawa:6|# &<{README} Check doc directory.It makes sense look at the bytes to see if we can infer an encoding, especially on Windows which doesn't use a default encoding. Currently Kawa checks for a byte-order mark; more
sniffingis likely to be added later.
What if the file is not a text file?
It might be reasonable to be able to configure
a handler for binary files.
For example for a .jpg
image file,
if the the console can display images, it makes sense to display
the image inline. It helps if the blob has a known MIME type.
(I believe a rich text console should be built using web browser technologies,
but that's a different topic.)
Writing to a file
The &<{..}
operation can be used with set!
to replace the contents of a file:
(set! &<{README} "Check example.com\n")
If you dislike using
as an output operator,
you can instead using the <
&>{..}
operation,
which evaluates to function whose single argument is the new value:
(&>{README} "Check example.com\n")
You can use &>>
to append more data to a file:
(&>>{README} "or check example2.com\n")
The current directory
Functions like path-data
or open-input-file
or the sugar
we seen above all use the current directory
as a base directory for relative pathname. You get the
current value of the current directory with the expression
(current-path)
.
This returns a path
object, which prints as the string value of the path.
The initial value of (current-path)
is the
value of the "user.dir"
property,
but you change it using a setter:
(set! (current-path) "/opt/myApp/")A string value is automatically converted to a
path
,
normally a filepath
.
The procedure current-path
is a
parameter,
so you can alternatively call it with the new value:
(current-path "/opt/myApp/")You can also use the
parameterize
form:
(parameterize ((current-path "/opt/myApp/")) (list &<{data1.txt} &<{data2.txt}))