LISP - Writing Data to Streams
Lisp - Writing Data to Streams
Section titled “Lisp - Writing Data to Streams”Writing data is a fundamental I/O operation. In Common Lisp, all output is directed to streams. A stream is a source or sink of data, such as the console, a file, or a string. This tutorial explores modern best practices for writing data.
Understanding Output Streams
Section titled “Understanding Output Streams”Lisp manages several standard streams. The most important one for output is *standard-output*, which typically defaults to your terminal or console (REPL).
Core Output Functions: Choosing the Right Tool
Section titled “Core Output Functions: Choosing the Right Tool”Lisp provides several functions for writing. The key is to choose the one that matches your intent.
princ: Writes an object in a human-readable format. For example, it prints strings without the surrounding quotes. Use this for generating clean output for users.(princ "Hello, World!")->Hello, World!print: Writes an object in a Lisp-readable format, preceded by a newline and followed by a space. It includes quotes for strings and is suitable for debugging or output that Lisp’sreadfunction could potentially parse back.(print "Hello, Lisp!")->(newline)"Hello, Lisp!"write: The master function, highly customizable with keyword arguments.printandprincare essentially convenient presets forwrite. Usewritewhen you need fine-grained control over the output format.format: The most powerful and flexible tool. It creates formatted text using a control string and arguments, similar toprintfin C. It’s the ideal choice for composing complex messages, reports, or any structured text.
Practical Example 1: Writing to the Console with format
Section titled “Practical Example 1: Writing to the Console with format”format is the idiomatic way to produce structured output. The first argument t directs output to *standard-output*.
main.lisp
Section titled “main.lisp”(let ((user "Alex") (items-count 5)) (format t "Welcome, ~a!~%You have ~d items in your cart.~%" user items-count))
;; Directives:;; ~a - Aesthetic: prints in a human-readable way (like princ).;; ~d - Decimal: prints an integer.;; ~% - Tilde-percent: prints a newline character.Output
Section titled “Output”Welcome, Alex!You have 5 items in your cart.Practical Example 2: Writing to a File
Section titled “Practical Example 2: Writing to a File”The modern, safe, and recommended way to write to a file is with the with-open-file macro. It automatically handles opening the file and, crucially, ensures the stream is closed when the block is exited, even if an error occurs.
main.lisp
Section titled “main.lisp”;; Create a log file named 'app.log'(with-open-file (log-stream "app.log" :direction :output :if-exists :supersede :if-does-not-exist :create) (format log-stream "Log started at ~a~%" (get-universal-time)) (princ "Application event: User logged in." log-stream) (terpri log-stream) ; terpri = terminate print (writes a newline) (format log-stream "Log finished."))
(print "Log file 'app.log' has been written.")Content of app.log
Section titled “Content of app.log”Log started at 3855138000Application event: User logged in.Log finished.Key Considerations and Best Practices
Section titled “Key Considerations and Best Practices”- Use
with-open-file: Always prefer this macro over manually callingopenandclosefor file I/O. It prevents resource leaks. - Buffering: Output to streams is often buffered for efficiency. If you need to ensure data is written immediately (e.g., to a log file before a potential crash), call
(finish-output stream)or(force-output stream). - Writing to Strings: To capture output into a string instead of a file or console, use the
with-output-to-stringmacro. Example:(with-output-to-string (s) (format s "Hello, ~a" "string"))returns"Hello, string". - Error Handling: File operations can fail (e.g., disk full, no permissions). Wrap
with-open-filein ahandler-caseblock to gracefully handle potentialfile-errorconditions in robust applications.