190 likes | 363 Views
Formatted I/O. The read and print statements are very limited they default to using the standard input and output devices if we want to redirect I/O, we have to specify the proper input or output stream if we want to generate formatted output, we have to use the format command
E N D
Formatted I/O • The read and print statements are very limited • they default to using the standard input and output devices • if we want to redirect I/O, we have to specify the proper input or output stream • if we want to generate formatted output, we have to use the format command • So we take a look at streams and format here • format is very similar to C’s printf statement • (format stream string vars) • Stream is the stream that you want to redirect the output to • use T to send it to standard output, use nil if you instead want format to return the formatted output as a string to be stored • Format returns nil unless you specify nil as the stream in which case format returns the string – confused? • string and vars are covered next
Specification String • The string supplied to format specifies any literal output that you want as well as the format to be applied to any variables • the variables are optional, but you need to have one variable for every variable specifier in the specification string • Here is a simple example where ~D is the specification for an integer (decimal) value: • (format t “the value of x is ~D and the value of y is ~D” x y) • if x = 5 and y = 10, this will output • the value of x is 5 and the value of y is 10 • if you only supplied variable x, you would get an error • if you supplied more than 2 variables, the excess would be ignored • All specifications will start with ~ and be followed by one or more items: the directive (the way to interpret the datum), and optional arguments such as minimum width specification, justification specification, etc • this gets complicated, so we will look at examples as we see these various specifiers and options
Simple Directives • ~~ — output a ~ • ~% — output a carriage return (start a new line) • ~& — output a fresh line (if we aren’t at the start of a new line, start a new line) • ~n% and ~n& will output n line breaks/new lines • ~| — page break, may or may not work depending on the CL system you are using • ~T – tab, ~nT – tab over n spaces • ~D – output a decimal integer • ~wD – output a decimal integer with at least w characters (pads the output with blanks) • (format t “~10D” 345) bbbbbbb345 (b = blank) • ~w,’cD – where c is a character, is the same as ~wD except that the character supplied is used for padding • (format t “~10,’*D” 345) *******345 • ~@D – output the decimal with a + sign for positive (- for negative is always provided with the D argument)
More Directives • For floating point numbers use: • ~F – for normal floating point • ~E – for scientific notation • ~G – use ~F or ~E, which ever will provide the shorter notation • ~$ – use monetary notation (as in xxx.xx) but without a dollar sign • (format t “~$” 32.876) 32.88 • Each of the above permits two width specifiers • the first denotes the total minimum width • the second denotes the number of decimal point digits • the two specifiers are separated by a , • (format t “~10,2F” 1099.87654) bbb1099.88 • (format t “~10,2E” 1099.87654) bbb1.10E+3 • To translate a number into another base you can use: • ~O – octal • ~X – hex • ~B – binary • ~bR – base b integer • (format t “~6R” 987) 4323 • (format t “~X” 1000) 3E8
Yet More Directives! • Here’s where things can get fun: • ~R – spell an integer out by name • (format t “~R” 987) nine hundred and eighty-seven • ~:R – add an ending to the spelled out integer like th • (format t “~:R” 987) nine hundred and eighty-seventh • ~@R – spell out as Roman numerals • (format t “~@R” 987) CMLXXXVII • ~P – pluralize the output • (format t “~D bottle~P of beer on the wall” X X) • If X = 1 1 bottle of beer on the wall • If X = 10 10 bottles of beer on the wall • If you use ~:P instead of ~P, then you only need to supply the argument once as in (format t “~D bottle~:P of beer on the wall” X) • ~@P is a variation that chooses whether to use y or ies for an ending • (format t “do you trust your memor~@P?” X)
Directives for Lists of Values • What if you wanted to supply a list and have each item output, but you are unsure as you write your statement how many items will appear in the list? • You could use a loop: • (dolist (x (butlast lis)) (format t “~D, ” x)) (format t “~D” (car (last lis))) • But format gives you a better way to process a list by using ~{ and ~} • (format t “~{~D, ~}~D” (butlast lis) (car (last lis))) • notice that (butlast lis) returns all but the last list item, these are all printed as ~D, or a number followed by a , and a blank, after the list directive ends, we print one more ~D for (car (last lis)) • If we didn’t care about the commas separating the numbers, we could simply to • (format t “~{~D ~}” lis)
Conditional Printing • Imagine that you don’t know which item to print out of a list of options, we can use a conditional statement or a conditional directive • Example: You ask the user for a number, 0, 1 or 2, and are going to print “hello”, “hi”, or “shalom” • (cond ((= x 0) (format t “hello”)) ((= x 1) (format t “hi”)) (t (format t “shalom”))) • Or you can use the ~[ ~] directive by inserting the options inside the [ ] as value~;value~; • (format t “~[hello~;hi~;shalom~]” x) • A binary condition can be presented using ~:[falseoutput~;trueoutput~] • (format t “you ~:[may not~;may~] drink” (>= age 21)) • You may also supply ~@[format~] which takes the next argument and, if not nil, processes it, if nil, discards it • (format t “~{~@[~A~]~}” lis) outputs all items in lis one at a time with the exception of any nil items, which are not output, merely ignored
Characters, Strings and Miscellany • ~C is used to output a character • ~A is used to output strings, but can be used to output any type, so if you are unsure of the data type, use ~A • ~S is the same as ~A except that escape characters are properly output • ~( … ~) outputs any text in lower case • ~:@(...~) outputs text in upper case • ~:(…~) outputs text in lower case with each new word capitalized (title format) • ~@(...~) outputs text in lower case except the first word which is capitalized (sentence format) • ~< …~> right justifies whatever is inside the brackets, or • ~w<…~> right justifies to a width of size w (w being an int value) • ~V forces the next argument to be used as part of the next directive • (format t “~D ~VD ~VD” 10 5 100 8 1000) 10 100 1000
One Last Example • Imagine that you have two lists: names and addresses • You want to output them row by row in name/address pairs • You can write a loop to accomplish this: • (dotimes (i (length names)) (format t "~%~A~10T~A" (nth i names) (nth i addresses))) • But you could not do this: • (format t “~%~{~A~10T~A~}” names addresses) • because the ~{ ~} empties one list, so you would wind up getting the first 2 names on one line, the next two on the next line, etc, and no addresses • However, you can get around that problem by pairing up the two lists • (setf temp nil) • (dotimes (i (length names)) (setf temp (append temp (list (nth i names)) (list (nth i addresses))))) • (format t “~{~%~A~10T~A~}” temp)
The Last Word on Format • All of our examples to this point have used t as the first parameter to format • This indicates that the output should be sent to the standard-output stream, which is the same stream that print uses • So now we have a better way to output than print • We can use different arguments for this parameter • (setf x (format nil “…”)) returns a string which in this case is stored in x • (format *new-stream* “…”) outputs to *new-stream* • what is *new-stream*? it is a stream that has been defined elsewhere in the code and could represent other windows or files • so next, we look at defining and using streams
Using Streams • All of the input and output instructions can take a stream as an argument and the input/output will be directed to that stream • Built-in streams are: • *standard-input* -- this is what read defaults to using, and is the keyboard in most systems • *standard-output* -- this is what print and format default to using, and is the REPL environment in most systems • *error-output* -- all error messages are sent here, it defaults to *standard-output* but can be redirected, to say a file or another window • *query-io* -- any user-prompt functions send output here and echo input here, this is the case with the function yes-or-no-p, defaults to a pop-up window in LispWorks • *debug-io* -- interactive debugging, defaults to *standard-output* in most cases • *trace-output* -- used by the trace function • *standard-input* and *standard-output* are usually assigned to *terminal-io*, which is the system’s default input and output (keyboard/monitor)
Input Functions • We’ve seen read, but in reality it is more complex: • (read &optional input-stream eof-error-p eof-value recursive-p) • ignoring the last item, eof-error-p returns whether an error arose or not and eof-value contains the type of error • input-stream is whatever stream you want to redirect the input from, for instance a file • read-preserving-whitespace – is read but where whitespace is preserved in the input (this allows code to examine the input and see if it was terminated by a space, an enter key, or other) – whitespace by the way is not just blank spaces, it includes the tab key, enter key, etc • read-line – inputs all characters up to a new line, often used for input from text file • read-char – input a single character • unread-char – for a text file, resets the file pointer to be one previous (in effect, nullifies the most recent read-char operation) • peek-char – returns the next character, but leaves file pointer at the current location • Like read, all of these can have an optional stream to redirect the input and the non-char functions also allow the other optional params (eof, etc)
Output Functions • As with input functions, all of these (including print) can take an optional output stream • Without the output stream, it defaults to *standard-output* • (print object stream) • prin1 is the same as print but print automatically outputs a new line character first, prin1 does not • princ is the same as prin1 except that all escape characters are suppressed • pprint is the same as print except that trailing space is omitted • both prin1 and print are appropriate to generate output that can be read by a read statement, princ cannot • There are also a group of write functions • (write object :stream stream) however these also have several other keyword arguments available: • :escape, :radix, :base, :circle, :pretty, :level, :length, :case, :gensym, :array • we won’t cover what most of these do, but :circle set to nil means that if a list has circularity, then the output will stop after one traversal of the list rather than infinitely repeating it! • in addition, there are write-char, write-string, write-line, prin1-to-string, princ-to-string functions
Miscellany • yes-or-no-p, y-or-n-p – as stated earlier, this function allows you to send a string prompt to the user and receive a yes or no reply • the first function expects the word “yes” or “no”, the latter can take characters or words, both will return T if yes/y is typed in (or clicked if a window pops up) or nil • parse-integer – accepts a string that should be an integer in quotes and returns the appropriate integer value • permits keyword parameters :start, :end :radix, :junk-allowed • assume date stores a date as “xx/xx/xxxx”, we want to add one to the year: • (+ (parse-integer date :start 6) 1) – without :end, it goes from the start point onward • clear-input – clears any buffer associated with the given input stream • terpri – outputs a new line to the given stream • fresh-line – outputs a new line if we aren’t already at the beginning of a new line • finish-output, force-output and clear-output are all available similar to clear-input • read-byte, write-byte – to input/output bytes (binary values) from/to a binary file instead of a text file
Files • Like Java, CL allows you to open, write to/read from, and close files using streams • To open a file, use (open filename) • Open has keyword arguments for :direction :element-type :if-exists and :if-does-not-exist • :direction expects one of :input, :output, :io, :probe (does the file exist? without opening it) • :element-type allows you to specify the expected type of datum to be input/output from/to this file, these types are recognized: character, integer, string-char, bit and :default • :if-exists and :if-does-not-exist allow you to specify what to do if the file exists/does not exist: :error, :new-version, :rename, :rename-and-delete, :overwrite, :append, :create, :supersede, nil (don’t do anything, simply return nil as failure) • Open returns the open stream, but in order to access it you need to store the stream as in (setf infile (open “…” :direction :input :if-does-not-exist nil)) • To close a file, (close stream) • To access the stream, use any of the input or output functions, specifying the stream in the function as in (read infile)
Example • Imagine that you have an input file that consists of numeric data • You want to input each datum and output it to another text file with a description of each datum (for instance, “this is the tenth datum: 31”) • The following code will do this (setf infile (open "c:\\csc375\\data.txt")) (setf outfile (open "c:\\csc375\\output.txt" :direction :output :if-exists :supersede)) (do ((i 1 (+ 1 i)) (temp (read infile nil t) (read infile nil t))) ((not (numberp temp))) (format outfile “This is the ~:R datum: ~A~%" i temp)) (close infile) (close outfile)
Other Stream Functions • with-open-file: this allows you to open a file in a block such that any I/O operations in the block default to using the stream you have opened • rename-file: allows you to rename a file to a new name • delete-file: allows you to delete a given file • file-position: returns the current position, or allows you to change the current position • probe-file: allows you to determine if a file currently exists by the given name • file-write-date: returns the file system’s date/time that a given file was created or last written to • file-author: same but returns the author of the file • file-length: returns the length of a given file as an integer (if the file is a binary file, this is the number of units as measured by the argument :element-type) • load: this loads the given filename into the CL environment, interpreting each item in the file as they are read in • this allows you to write functions and other definitions in a file and load them in prior to starting a given session