common-lisp
Strumienie
Szukaj…
Składnia
-
(read-char &optional stream eof-error-p eof-value recursive-p)
=> znak -
(write-char character &optional stream)
=> znak -
(read-line &optional stream eof-error-p eof-value recursive-p)
=> line, missing-newline-p -
(write-line line &optional stream)
=> linia
Parametry
Parametr | Szczegół |
---|---|
stream | Strumień do odczytu lub zapisu. |
eof-error-p | Powinien być sygnalizowany błąd w przypadku napotkania końca pliku. |
eof-value | Jaką wartość należy zwrócić w przypadku napotkania eof-error-p , a eof-error-p ma wartość false. |
recursive-p | Czy operacja odczytu jest wywoływana rekurencyjnie z READ . Zwykle należy to pozostawić jako NIL . |
character | Znak do napisania lub znak, który został odczytany. |
line | Linia do zapisu lub linia, która została przeczytana. |
Tworzenie strumieni wejściowych z ciągów
Makro WITH-INPUT-FROM-STRING
można wykorzystać do utworzenia strumienia z łańcucha.
(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
To samo można zrobić ręcznie za pomocą 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)))
Zapisywanie danych wyjściowych do łańcucha
Makra WITH-OUTPUT-TO-STRING
można użyć do utworzenia strumienia wyjściowego łańcucha i zwrócenia wynikowego łańcucha na końcu.
(with-output-to-string (str)
(write-line "Foobar!" str)
(write-string "Barfoo!" str))
;=> "Foobar!
; Barfoo!"
To samo można zrobić ręcznie, używając MAKE-STRING-OUTPUT-STREAM
i GET-OUTPUT-STREAM-STRING
.
(let ((str (make-string-output-stream)))
(write-line "Foobar!" str)
(write-string "Barfoo!" str)
(get-output-stream-string str))
Szare strumienie
Szare strumienie są niestandardowym rozszerzeniem, które umożliwia strumienie zdefiniowane przez użytkownika. Zapewnia klasy i metody, które użytkownik może rozszerzyć. Powinieneś sprawdzić instrukcję implementacji, aby sprawdzić, czy zapewnia ona strumienie Graya.
W prostym przykładzie strumień wejściowy znaków, który zwraca losowe znaki, można zaimplementować w następujący sposób:
(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
Odczytywanie pliku
Plik można otworzyć do odczytu jako strumień przy użyciu makra 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
To samo można zrobić ręcznie, używając OPEN
i 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)))
Zauważ, że READ-LINE
tworzy nowy ciąg dla każdej linii. To może być powolne. Niektóre implementacje zapewniają wariant, który może wczytywać linię do bufora łańcuchów. Przykład: READ-LINE-INTO
dla Allegro CL.
Zapis do pliku
Plik można otworzyć w celu zapisania jako strumień przy użyciu makra 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)))
To samo można zrobić ręcznie za pomocą OPEN
i 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))
Kopiowanie pliku
Skopiuj bajt na bajt pliku
Poniższa funkcja kopiuje plik do innego, wykonując dokładną kopię bajt po bajcie, ignorując rodzaj zawartości (która może być albo wierszami znaków w danych kodowania lub danymi binarnymi):
(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))))))
Typ (unsigned-byte 8)
to typ 8-bitowych bajtów. Funkcje read-byte
i write-byte
działają na bajtach, zamiast read-char
i write-char
które działają na znakach. read-byte
zwraca bajt odczytany ze strumienia lub NIL
na końcu pliku, jeśli drugim opcjonalnym parametrem jest NIL
(w przeciwnym razie sygnalizuje błąd).
Kopia zbiorcza
Dokładna kopia, bardziej wydajna od poprzedniej. można to zrobić, odczytując i zapisując pliki z dużymi porcjami danych za każdym razem, zamiast pojedynczych bajtów:
(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
są tutaj używane z buforem, który jest wektorem bajtów (mogą działać na sekwencji bajtów lub znaków). read-sequence
wypełnia tablicę za każdym razem odczytanymi bajtami i zwraca liczbę odczytanych bajtów (która może być mniejsza niż rozmiar tablicy po osiągnięciu końca pliku). Zauważ, że tablica jest destrukcyjnie modyfikowana przy każdej iteracji.
Dokładne kopiowanie linia po linii pliku
Ostatnim przykładem jest kopia wykonywana przez odczytanie każdego wiersza znaków pliku wejściowego i zapisanie go w pliku wyjściowym. Zauważ, że ponieważ potrzebujemy dokładnej kopii, musimy sprawdzić, czy ostatni wiersz pliku wejściowego jest zakończony, czy nie znakiem końca wiersza. Z tego powodu używamy dwóch wartości zwracanych przez read-line
: nowy ciąg znaków zawierający znaki następnego wiersza oraz wartość logiczna, która jest prawdziwa, jeśli wiersz jest ostatnim plikiem i nie zawiera ostatniego znaku nowej linii (s). W tym przypadku zamiast write-line
jest write-string
write-line
, ponieważ ten pierwszy nie dodaje nowego wiersza na końcu wiersza.
(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)))))))))
Należy pamiętać, że ten program jest niezależny od platformy, ponieważ znak (znaki) nowej linii (różne w różnych systemach operacyjnych) jest automatycznie zarządzany przez funkcje read-line
write-line
.
Odczytywanie i zapisywanie całych plików do iz ciągów
Poniższa funkcja odczytuje cały plik do nowego ciągu i zwraca go:
(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))))
Wynik to NIL
jeśli plik nie istnieje.
Poniższa funkcja zapisuje ciąg do pliku. Parametr słowa kluczowego służy do określenia, co należy zrobić, jeśli plik już istnieje (domyślnie powoduje błąd, dopuszczalne są wartości makra 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)))
W takim przypadku write-sequence
można zastąpić write-string
.