common-lisp
Streams
Поиск…
Синтаксис
-
(read-char &optional stream eof-error-p eof-value recursive-p)
=> character -
(write-char character &optional stream)
=> символ -
(read-line &optional stream eof-error-p eof-value recursive-p)
=> line, missing-newline-p -
(write-line line &optional stream)
=> строка
параметры
параметр | подробность |
---|---|
stream | Поток для чтения или записи. |
eof-error-p | Если сообщение об ошибке будет сообщено, если обнаружен конец файла. |
eof-value | Какое значение должно быть возвращено, если eof встречается, а eof-error-p - false. |
recursive-p | Является ли операция чтения называется рекурсивно из READ . Обычно это следует оставить как NIL . |
character | Персонаж для записи или символ, который был прочитан. |
line | Строка для записи или строка, которая была прочитана. |
Создание входных потоков из строк
Макрос WITH-INPUT-FROM-STRING
может использоваться для создания потока из строки.
(with-input-from-string (str "Foobar")
(loop for i from 0
for char = (read-char str nil nil)
while char
do (format t "~d: ~a~%" i char)))
; 0: F
; 1: o
; 2: o
; 3: b
; 4: a
; 5: r
;=> NIL
То же самое можно сделать вручную, используя MAKE-STRING-INPUT-STREAM
.
(let ((str (make-string-input-stream "Foobar")))
(loop for i from 0
for char = (read-char str nil nil)
while char
do (format t "~d: ~a~%" i char)))
Запись вывода в строку
Макрос WITH-OUTPUT-TO-STRING
можно использовать для создания потока вывода строки и возвращать результирующую строку в конце.
(with-output-to-string (str)
(write-line "Foobar!" str)
(write-string "Barfoo!" str))
;=> "Foobar!
; Barfoo!"
То же самое можно сделать вручную с помощью MAKE-STRING-OUTPUT-STREAM
и GET-OUTPUT-STREAM-STRING
.
(let ((str (make-string-output-stream)))
(write-line "Foobar!" str)
(write-string "Barfoo!" str)
(get-output-stream-string str))
Серые потоки
Серые потоки - это нестандартное расширение, которое позволяет определять потоки, определенные пользователем. Он предоставляет классы и методы, которые пользователь может расширить. Вы должны проверить свое руководство по реализации, чтобы убедиться, что оно обеспечивает потоки Gray.
Для простого примера поток ввода символов, который возвращает случайные символы, может быть реализован следующим образом:
(defclass random-character-input-stream (fundamental-character-input-stream)
((character-table
:initarg :character-table
:initform "abcdefghijklmnopqrstuvwxyz
" ; The newline is necessary.
:accessor character-table))
(:documentation "A stream of random characters."))
(defmethod stream-read-char ((stream random-character-input-stream))
(let ((table (character-table stream)))
(aref table (random (length table)))))
(let ((stream (make-instance 'random-character-input-stream)))
(dotimes (i 5)
(print (read-line stream))))
; "gyaexyfjsqdcpciaaftoytsygdeycrrzwivwcfb"
; "gctnoxpajovjqjbkiqykdflbhfspmexjaaggonhydhayvknwpdydyiabithpt"
; "nvfxwzczfalosaqw"
; "sxeiejcovrtesbpmoppfvvjfvx"
; "hjplqgstbodbalnmxhsvxdox"
;=> NIL
Чтение файла
Файл можно открыть для чтения в виде потока, используя макрос WITH-OPEN-FILE
.
(with-open-file (file #P"test.file")
(loop for i from 0
for line = (read-line file nil nil)
while line
do (format t "~d: ~a~%" i line)))
; 0: Foobar
; 1: Barfoo
; 2: Quuxbar
; 3: Barquux
; 4: Quuxfoo
; 5: Fooquux
;=> T
То же самое можно сделать вручную с помощью OPEN
и CLOSE
.
(let ((file (open #P"test.file"))
(aborted t))
(unwind-protect
(progn
(loop for i from 0
for line = (read-line file nil nil)
while line
do (format t "~d: ~a~%" i line))
(setf aborted nil))
(close file :abort aborted)))
Обратите внимание, что READ-LINE
создает новую строку для каждой строки. Это может быть медленным. Некоторые реализации предоставляют вариант, который может читать строку в строковый буфер. Пример: READ-LINE-INTO
для Allegro CL.
Запись в файл
Файл можно открыть для записи в виде потока, используя макрос WITH-OPEN-FILE
.
(with-open-file (file #P"test.file" :direction :output
:if-exists :append
:if-does-not-exist :create)
(dolist (line '("Foobar" "Barfoo" "Quuxbar"
"Barquux" "Quuxfoo" "Fooquux"))
(write-line line file)))
То же самое можно сделать вручную с помощью OPEN
и CLOSE
.
(let ((file (open #P"test.file" :direction :output
:if-exists :append
:if-does-not-exist :create)))
(dolist (line '("Foobar" "Barfoo" "Quuxbar"
"Barquux" "Quuxfoo" "Fooquux"))
(write-line line file))
(close file))
Копирование файла
Копировать байт за байт файла
Следующая функция копирует файл в другой, выполняя точную копию байта за байт, игнорируя вид контента (который может быть либо строками символов в некоторых кодировках, либо двоичными данными):
(defun byte-copy (infile outfile)
(with-open-file (instream infile :direction :input :element-type '(unsigned-byte 8)
:if-does-not-exist nil)
(when instream
(with-open-file (outstream outfile :direction :output :element-type '(unsigned-byte 8)
:if-exists :supersede)
(loop for byte = (read-byte instream nil)
while byte
do (write-byte byte outstream))))))
Тип (unsigned-byte 8)
- это тип 8-разрядных байтов. Функции read-byte
и write-byte
работают в байтах, а не read-char
и write-char
которые работают с символами. read-byte
возвращает байт, считанный из потока, или NIL
в конце файла, если вторым необязательным параметром является NIL
(иначе он сигнализирует об ошибке).
Массовая копия
Точная копия, более эффективная предыдущая. может быть сделано путем чтения и записи файлов с большими фрагментами данных каждый раз, вместо одиночных байтов:
(defun bulk-copy (infile outfile)
(with-open-file (instream infile :direction :input :element-type '(unsigned-byte 8)
:if-does-not-exist nil)
(when instream
(with-open-file (outstream outfile :direction :output :element-type '(unsigned-byte 8)
:if-exists :supersede)
(let ((buffer (make-array 8192 :element-type '(unsigned-byte 8))))
(loop for bytes-read = (read-sequence buffer instream)
while (plusp bytes-read)
do (write-sequence buffer outstream :end bytes-read)))))))
read-sequence
write-sequence
используются здесь с буфером, который представляет собой вектор байтов (они могут работать с последовательностями байтов или символов). read-sequence
заполняет массив байтами, считываемыми каждый раз, и возвращает количество прочитанных байтов (которое может быть меньше размера массива, когда достигнут конец файла). Обратите внимание, что массив подвергается деструктивной модификации на каждой итерации.
Точная копия строки в строке файла
Последний пример - это копия, выполняемая путем чтения каждой строки символов входного файла и записи ее в выходной файл. Обратите внимание: поскольку мы хотим получить точную копию, мы должны проверить, завершена ли последняя строка входного файла или нет символом конца строки. По этой причине мы используем два значения, возвращаемые read-line
: новая строка, содержащая символы следующей строки, и логическое значение, которое истинно, если строка является последней из файла и не содержит окончательный символ новой строки (ы). В этом случае вместо write-string
используется write-string
write-line
, так как первая не добавляет новую строку в конце строки.
(defun line-copy (infile outfile)
(with-open-file (instream infile :direction :input :if-does-not-exist nil)
(when instream
(with-open-file (outstream outfile :direction :output :if-exists :supersede)
(let (line missing-newline-p)
(loop
(multiple-value-setq (line missing-newline-p)
(read-line instream nil nil))
(cond (missing-newline-p ; we are at the end of file
(when line (write-string line outstream)) ; note `write-string`
(return)) ; exit from simple loop
(t (write-line line outstream)))))))))
Обратите внимание, что эта программа независима от платформы, так как символ новой строки (меняющийся в разных операционных системах) автоматически управляется функциями read-line
и write-line
.
Чтение и запись целых файлов в строки и из них
Следующая функция считывает весь файл в новую строку и возвращает его:
(defun read-file (infile)
(with-open-file (instream infile :direction :input :if-does-not-exist nil)
(when instream
(let ((string (make-string (file-length instream))))
(read-sequence string instream)
string))))
Результатом является NIL
если файл не существует.
Следующая функция записывает строку в файл. Параметр ключевого слова используется для указания того, что делать, если файл уже существует (по умолчанию он вызывает ошибку, допустимые значения являются значениями макроса with-open-file
).
(defun write-file (string outfile &key (action-if-exists :error))
(check-type action-if-exists (member nil :error :new-version :rename :rename-and-delete
:overwrite :append :supersede))
(with-open-file (outstream outfile :direction :output :if-exists action-if-exists)
(write-sequence string outstream)))
В этом случае write-sequence
может быть заменена на write-string
.