Книга: Practical Common Lisp
Saving and Loading the Database
Saving and Loading the Database
Having a convenient way to add records to the database is nice. But it's not so nice that the user is going to be very happy if they have to reenter all the records every time they quit and restart Lisp. Luckily, with the data structures you're using to represent the data, it's trivially easy to save the data to a file and reload it later. Here's a save-db
function that takes a filename as an argument and saves the current state of the database:
(defun save-db (filename)
(with-open-file (out filename
:direction :output
:if-exists :supersede)
(with-standard-io-syntax
(print *db* out))))
The WITH-OPEN-FILE
macro opens a file, binds the stream to a variable, executes a set of expressions, and then closes the file. It also makes sure the file is closed even if something goes wrong while evaluating the body. The list directly after WITH-OPEN-FILE
isn't a function call but rather part of the syntax defined by WITH-OPEN-FILE
. It contains the name of the variable that will hold the file stream to which you'll write within the body of WITH-OPEN-FILE
, a value that must be a file name, and then some options that control how the file is opened. Here you specify that you're opening the file for writing with :direction :output
and that you want to overwrite an existing file of the same name if it exists with :if-exists :supersede
.
Once you have the file open, all you have to do is print the contents of the database with (print *db* out)
. Unlike FORMAT
, PRINT
prints Lisp objects in a form that can be read back in by the Lisp reader. The macro WITH-STANDARD-IO-SYNTAX
ensures that certain variables that affect the behavior of PRINT
are set to their standard values. You'll use the same macro when you read the data back in to make sure the Lisp reader and printer are operating compatibly.
The argument to save-db
should be a string containing the name of the file where the user wants to save the database. The exact form of the string will depend on what operating system they're using. For instance, on a Unix box they should be able to call save-db
like this:
CL-USER> (save-db "~/my-cds.db")
((:TITLE "Lyle Lovett" :ARTIST "Lyle Lovett" :RATING 9 :RIPPED T)
(:TITLE "Give Us a Break" :ARTIST "Limpopo" :RATING 10 :RIPPED T)
(:TITLE "Rockin' the Suburbs" :ARTIST "Ben Folds" :RATING 6 :RIPPED
T)
(:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 9 :RIPPED T))
On Windows, the filename might be something like "c:/my-cds.db
" or "c:my-cds.db
."[27]
You can open this file in any text editor to see what it looks like. You should see something a lot like what the REPL prints if you type *db*
.
The function to load the database back in is similar.
(defun load-db (filename)
(with-open-file (in filename)
(with-standard-io-syntax
(setf *db* (read in)))))
This time you don't need to specify :direction
in the options to WITH-OPEN-FILE
, since you want the default of :input
. And instead of printing, you use the function READ
to read from the stream in
. This is the same reader used by the REPL and can read any Lisp expression you could type at the REPL prompt. However, in this case, you're just reading and saving the expression, not evaluating it. Again, the WITH-STANDARD-IO-SYNTAX
macro ensures that READ
is using the same basic syntax that save-db
did when it PRINT
ed the data.
The SETF
macro is Common Lisp's main assignment operator. It sets its first argument to the result of evaluating its second argument. So in load-db
the *db*
variable will contain the object read from the file, namely, the list of lists written by save-db
. You do need to be careful about one thing—load-db
clobbers whatever was in *db*
before the call. So if you've added records with add-record
or add-cds
that haven't been saved with save-db
, you'll lose them.
- Информация заголовочной страницы (Database header)
- Database dialect
- Разработка приложений баз данных InterBase на Borland Delphi
- 4.4.4 The Dispatcher
- DATABASE CACHE SIZE
- Open Source Insight and Discussion
- Introduction to Microprocessors and Microcontrollers
- About the author
- Chapter 6. Traversing of tables and chains
- Chapter 7. The state machine
- Chapter 8. Saving and restoring large rule-sets
- Chapter 11. Iptables targets and jumps