common-lisp
Streams
Suche…
Syntax
-
(read-char &optional stream eof-error-p eof-value recursive-p)
=> Zeichen -
(write-char character &optional stream)
=> Zeichen -
(read-line &optional stream eof-error-p eof-value recursive-p)
=> line, missing-newline-p -
(write-line line &optional stream)
=> Zeile
Parameter
Parameter | Detail |
---|---|
stream | Der Stream, aus dem gelesen oder geschrieben werden soll. |
eof-error-p | Sollte ein Fehler gemeldet werden, wenn das Dateiende aufgetreten ist. |
eof-value | Welcher Wert sollte zurückgegeben werden, wenn eof gefunden wird und eof-error-p falsch ist. |
recursive-p | Wird die Leseoperation rekursiv von READ aufgerufen. Normalerweise sollte dies als NIL belassen werden. |
character | Das zu schreibende Zeichen oder das gelesene Zeichen. |
line | Die zu schreibende Zeile oder die gelesene Zeile. |
Eingabeströme aus Strings erstellen
Das Makro WITH-INPUT-FROM-STRING
kann verwendet werden, um aus einem String einen Stream zu erstellen.
(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
Dasselbe kann manuell mit 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)))
Ausgabe in einen String schreiben
Das Makro WITH-OUTPUT-TO-STRING
kann verwendet werden, um einen String-Ausgabestrom zu erstellen und den resultierenden String am Ende zurückzugeben.
(with-output-to-string (str)
(write-line "Foobar!" str)
(write-string "Barfoo!" str))
;=> "Foobar!
; Barfoo!"
Dasselbe kann manuell mit MAKE-STRING-OUTPUT-STREAM
und GET-OUTPUT-STREAM-STRING
.
(let ((str (make-string-output-stream)))
(write-line "Foobar!" str)
(write-string "Barfoo!" str)
(get-output-stream-string str))
Graue Bäche
Gray-Streams sind eine nicht standardmäßige Erweiterung, die benutzerdefinierte Streams ermöglicht. Es bietet Klassen und Methoden, die der Benutzer erweitern kann. Sie sollten in Ihrem Implementierungshandbuch nachsehen, ob Gray-Streams verfügbar sind.
Für ein einfaches Beispiel könnte ein Zeicheneingabestrom, der zufällige Zeichen zurückgibt, folgendermaßen implementiert werden:
(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
Datei lesen
Eine Datei kann mit dem Makro WITH-OPEN-FILE
als Stream zum Lesen geöffnet werden.
(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
Dasselbe kann manuell mit OPEN
und 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)))
Beachten Sie, dass READ-LINE
für jede Zeile eine neue Zeichenfolge erstellt. Das kann langsam sein. Einige Implementierungen stellen eine Variante bereit, die eine Zeile in einen Zeichenkettenpuffer lesen kann. Beispiel: READ-LINE-INTO
für Allegro CL.
In eine Datei schreiben
Eine Datei kann zum Schreiben als Stream mit dem Makro WITH-OPEN-FILE
geöffnet werden.
(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)))
Dasselbe kann manuell mit OPEN
und 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))
Datei kopieren
Kopieren Sie Byte für Byte einer Datei
Die folgende Funktion kopiert eine Datei in eine andere, indem sie eine genaue Byte-pro-Byte-Kopie durchführt und dabei die Art des Inhalts ignoriert (dies können entweder Zeilen von Zeichen in einer Codierung oder binäre Daten sein):
(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))))))
Der Typ (unsigned-byte 8)
ist der Typ von 8-Bit-Bytes. Die Funktionen read-byte
und write-byte
arbeiten mit Bytes, anstatt mit read-char
und write-char
, die mit Zeichen arbeiten. read-byte
gibt ein aus dem Stream read-byte
oder NIL
am Ende der Datei zurück, wenn der zweite optionale Parameter NIL
(andernfalls wird ein Fehler gemeldet).
Massenkopie
Eine genaue Kopie, effizienter als die vorige. Dies kann durch Lesen und Schreiben der Dateien mit großen Datenblöcken jedes Mal statt einzelner Bytes erfolgen:
(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
und write-sequence
werden hier mit einem Puffer verwendet, der ein Vektor von Bytes ist (sie können Sequenzen von Bytes oder Zeichen verarbeiten). read-sequence
füllt das Array mit den gelesenen Bytes jedes Mal und gibt die Anzahl der gelesenen Bytes zurück (die Anzahl kann geringer sein als die Größe des Arrays, wenn das Dateiende erreicht ist). Beachten Sie, dass das Array bei jeder Iteration destruktiv geändert wird.
Exaktes Kopieren Zeile für Zeile einer Datei
Das letzte Beispiel ist eine Kopie, bei der jede Zeile der Zeichen der Eingabedatei gelesen und in die Ausgabedatei geschrieben wird. Beachten Sie, dass wir, da wir eine exakte Kopie wünschen, prüfen müssen, ob die letzte Zeile der Eingabedatei durch ein Zeilenendezeichen abgeschlossen ist oder nicht. Aus diesem Grund verwenden wir die zwei Werte, die von read-line
: eine neue Zeichenfolge, die die Zeichen der nächsten Zeile enthält, und einen booleschen Wert, der true ist , wenn die Zeile die letzte der Datei ist und nicht das letzte Zeilenvorschubzeichen enthält (s). In diesem Fall write-string
wird anstelle von write-line
, da erstere nicht eine neue Zeile am Ende der Zeile hinzufügen.
(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)))))))))
Beachten Sie, dass dieses Programm plattformunabhängig ist, da die Zeilenende- Marke (n) (variiert in verschiedenen Betriebssystemen) automatisch durch die verwaltet wird read-line
und write-line
- Funktionen.
Lesen und Schreiben ganzer Dateien in und aus Strings
Die folgende Funktion liest eine gesamte Datei in eine neue Zeichenfolge und gibt sie zurück:
(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))))
Das Ergebnis ist NIL
wenn die Datei nicht vorhanden ist.
Die folgende Funktion schreibt eine Zeichenfolge in eine Datei. Mit einem Schlüsselwortparameter wird angegeben, was zu tun ist, wenn die Datei bereits vorhanden ist (standardmäßig wird ein Fehler ausgegeben, die zulässigen Werte sind die des Makros 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)))
In diesem Fall write-sequence
kann substituiert sein mit write-string
.