common-lisp
Strömmar
Sök…
Syntax
-
(read-char &optional stream eof-error-p eof-value recursive-p)
=> tecken -
(write-char character &optional stream)
=> tecken -
(read-line &optional stream eof-error-p eof-value recursive-p)
=> rad, saknas-newline-p -
(write-line line &optional stream)
=> rad
parametrar
Parameter | Detalj |
---|---|
stream | Strömmen att läsa från eller skriva till. |
eof-error-p | Skulle ett fel signaleras om filens slut uppstår. |
eof-value | Vilket värde ska returneras om eof stöter på och eof-error-p är falsk. |
recursive-p | Kallas läsoperationen rekursivt från READ . Vanligtvis ska detta lämnas som NIL . |
character | Karaktären att skriva, eller karaktären som lästes. |
line | Raden som ska skrivas eller raden som lästes. |
Skapa input-strömmar från strängar
Makroen WITH-INPUT-FROM-STRING
kan användas för att skapa en ström från en sträng.
(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
Detsamma kan göras manuellt med hjälp av 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)))
Skriva utdata till en sträng
Makroen WITH-OUTPUT-TO-STRING
kan användas för att skapa en strängutström och returnera den resulterande strängen i slutet.
(with-output-to-string (str)
(write-line "Foobar!" str)
(write-string "Barfoo!" str))
;=> "Foobar!
; Barfoo!"
Detsamma kan göras manuellt med hjälp av MAKE-STRING-OUTPUT-STREAM
och GET-OUTPUT-STREAM-STRING
.
(let ((str (make-string-output-stream)))
(write-line "Foobar!" str)
(write-string "Barfoo!" str)
(get-output-stream-string str))
Grå strömmar
Grå strömmar är en icke-standardförlängning som tillåter användardefinierade strömmar. Det tillhandahåller klasser och metoder som användaren kan utöka. Du bör kolla implementeringshandboken för att se om den ger grå strömmar.
För ett enkelt exempel kan en teckeninmatningsström som returnerar slumpmässiga tecken implementeras på följande sätt:
(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
Läser fil
En fil kan öppnas för läsning som en ström med 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
Detsamma kan göras manuellt med OPEN
och 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)))
Observera att READ-LINE
skapar en ny sträng för varje rad. Detta kan vara långsamt. Vissa implementationer ger en variant som kan läsa en rad i en strängbuffert. Exempel: READ-LINE-INTO
för Allegro CL.
Skriva till en fil
En fil kan öppnas för skrivning som en ström med 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)))
Detsamma kan göras manuellt med OPEN
och 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))
Kopierar en fil
Kopiera byte-per-byte av en fil
Följande funktion kopierar en fil till en annan genom att utföra en exakt byte-per-byte-kopia, ignorera typen av innehåll (som kan vara antingen rader med tecken i någon kodning eller binär data):
(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))))))
Typen (unsigned-byte 8)
är typen av 8-bitarsbyte. Funktionerna read-byte
och write-byte
fungerar på byte istället för read-char
och write-char
som fungerar på tecken. read-byte
returnerar en byte-läsning från strömmen, eller NIL
i slutet av filen om den andra valfria parametern är NIL
(annars signalerar det ett fel).
Bulkkopia
En exakt kopia, effektivare den föregående. kan göras genom att läsa och skriva filerna med stora bitar data varje gång, istället för enstaka byte:
(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
och write-sequence
används här med en buffert som är en vektor av byte (de kan fungera på sekvenser av byte eller tecken). read-sequence
fyller matrisen med de byte som läses varje gång och returnerar antalet lästa byte (som kan vara mindre än storleken på matrisen när slutet på filen nås). Observera att matrisen är destruktivt modifierad vid varje iteration.
Exakt kopieringsrad per rad för en fil
Det sista exemplet är en kopia som utförs genom att läsa varje rad med tecken i inmatningsfilen och skriva den till utdatafilen. Observera att eftersom vi vill ha en exakt kopia måste vi kontrollera om den sista raden i inmatningsfilen är avslutad eller inte av ett slut på radtecken. Av den anledningen använder vi de två värden som returneras av read-line
: en ny sträng som innehåller tecknen på nästa rad och ett booleskt värde som är sant om linjen är den sista i filen och inte innehåller det sista nyradstecknet (s). I det här fallet write-string
används i stället för write-line
, eftersom den förstnämnda inte lägga till en ny rad i slutet av raden.
(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)))))))))
Observera att detta program är plattformsoberoende, eftersom radmatningstecken (s) (varierar i olika operativsystem) automatiskt hanteras av read-line
och write-line
Läsa och skriva hela filer till och från strängar
Följande funktion läser en hel fil i en ny sträng och returnerar den:
(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))))
Resultatet är NIL
om filen inte finns.
Följande funktion skriver en sträng till en fil. En nyckelordsparameter används för att specificera vad man ska göra om filen redan finns (som standard orsakar den ett fel, värden som är tillåtna är de för makro 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)))
I detta fall kan write-sequence
ersättas med write-string
.