add port->string, file->string, etc.

svn: r12388
This commit is contained in:
Matthew Flatt 2008-11-11 17:55:14 +00:00
parent 209b252f2c
commit a6d953b6f5
9 changed files with 363 additions and 16 deletions

View File

@ -44,20 +44,21 @@
(let ([c (read-bytes-avail! s src)]) (let ([c (read-bytes-avail! s src)])
(cond (cond
[(number? c) [(number? c)
(for-each (let loop ([dests dests])
(lambda (dest) (unless (null? dests)
(let loop ([start 0]) (let loop ([start 0])
(unless (= start c) (unless (= start c)
(let ([c2 (write-bytes-avail s dest start c)]) (let ([c2 (write-bytes-avail s (car dests) start c)])
(loop (+ start c2)))))) (loop (+ start c2)))))
dests) (loop (cdr dests))))
(loop)] (loop)]
[(procedure? c) [(procedure? c)
(let ([v (let-values ([(l col p) (port-next-location src)]) (let ([v (let-values ([(l col p) (port-next-location src)])
(c (object-name src) l col p))]) (c (object-name src) l col p))])
(for-each (let loop ([dests dests])
(lambda (dest) (write-special v dest)) (unless (null? dests)
dests)) (write-special v (car dests))
(loop (cdr dests)))))
(loop)] (loop)]
[else [else
;; Must be EOF ;; Must be EOF

View File

@ -10,7 +10,18 @@
fold-files fold-files
find-files find-files
pathlist-closure) pathlist-closure
file->string
file->bytes
file->value
file->lines
file->bytes-lines
display-to-file
write-to-file
display-list-to-file)
(require "private/portlines.ss")
;; utility: sorted dirlist so functions are deterministic ;; utility: sorted dirlist so functions are deterministic
(define (sorted-dirlist [dir (current-directory)]) (define (sorted-dirlist [dir (current-directory)])
@ -331,3 +342,95 @@
(loop2 base (if (or (member base r) (member base paths)) (loop2 base (if (or (member base r) (member base paths))
new (cons base new))) new (cons base new)))
(loop (cdr paths) (append (reverse new) r)))))))) (loop (cdr paths) (append (reverse new) r))))))))
(define (check-path who f)
(unless (path-string? f)
(raise-type-error who "path string" f)))
(define (check-file-mode who file-mode)
(unless (memq file-mode '(binary text))
(raise-type-error who "'binary or 'text" file-mode)))
(define (file->x who f file-mode read-x x-append)
(check-path who f)
(check-file-mode who file-mode)
(let ([sz (file-size f)])
(call-with-input-file*
f
#:mode file-mode
(lambda (in)
;; There's a good chance that `file-size' gets all the data:
(let ([s (read-x sz in)])
;; ... but double-check:
(let ([more (let loop ()
(let ([l (read-x 4096 in)])
(if (eof-object? l)
null
(cons l (loop)))))])
(if (null? more)
s
(apply x-append (cons s more)))))))))
(define (file->string f #:mode [mode 'binary])
(file->x 'file->string f mode read-string string-append))
(define (file->bytes f #:mode [mode 'binary])
(file->x 'file->bytes f mode read-bytes bytes-append))
(define (file->value f #:mode [file-mode 'binary])
(check-path 'file->value f)
(check-file-mode 'file->value file-mode)
(let ([sz (file-size f)])
(call-with-input-file*
f
#:mode file-mode
read)))
(define (file->x-lines who f line-mode file-mode read-line)
(check-path who f)
(check-mode who line-mode)
(check-file-mode who file-mode)
(call-with-input-file*
f
#:mode file-mode
(lambda (p) (port->x-lines who p line-mode read-line))))
(define (file->lines f #:line-mode [line-mode 'any] #:mode [file-mode 'binary])
(file->x-lines 'file->lines f line-mode file-mode read-line))
(define (file->bytes-lines f #:line-mode [line-mode 'any] #:mode [file-mode 'binary])
(file->x-lines 'file->bytes-lines f line-mode file-mode read-bytes-line))
(define (->file who f mode exists write)
(unless (path-string? f)
(raise-type-error who "path string" f))
(unless (memq mode '(binary text))
(raise-type-error who "'binary or 'text" mode))
(unless (memq exists '(error append update replace truncate truncate/replace))
(raise-type-error who "'error, 'append, 'update, 'replace, 'truncate, or 'truncate/replace" exists))
(call-with-output-file*
f
#:mode mode
#:exists exists
write))
(define (display-to-file s f
#:mode [mode 'binary]
#:exists [exists 'error])
(->file 'display-to-file f mode exists (lambda (p) (display s p))))
(define (write-to-file s f
#:mode [mode 'binary]
#:exists [exists 'error])
(->file 'write-to-file f mode exists (lambda (p) (write s p))))
(define (display-list-to-file l f
#:mode [mode 'binary]
#:exists [exists 'error]
#:separator [newline #"\n"])
(unless (list? l)
(raise-type-error 'display-list-to-file "list" l))
(->file 'display-list-to-file f mode exists
(lambda (p)
(do-lines->port l p newline))))

View File

@ -1,5 +1,37 @@
(module port scheme/base (module port scheme/base
(require mzlib/port) (require mzlib/port
"private/portlines.ss")
(provide (except-out (all-from-out mzlib/port) (provide (except-out (all-from-out mzlib/port)
strip-shell-command-start))) strip-shell-command-start)
port->string
port->bytes
port->lines
port->bytes-lines
display-list)
(define (port->string-port who p)
(unless (input-port? p)
(raise-type-error who "input-port" p))
(let ([s (open-output-string)])
(copy-port p s)
s))
(define (port->string [p (current-input-port)])
(get-output-string (port->string-port 'port->string p)))
(define (port->bytes [p (current-input-port)])
(get-output-bytes (port->string-port 'port->bytes p) #t))
(define (port->lines [p (current-input-port)] #:line-mode [mode 'any])
(port->x-lines 'port->lines p mode read-line))
(define (port->bytes-lines [p (current-input-port)] #:line-mode [mode 'any])
(port->x-lines 'port->bytes-lines p mode read-bytes-line))
(define (display-list l [p (current-output-port)] #:separator [newline #"\n"])
(unless (list? l)
(raise-type-error 'display-list "list" l))
(unless (output-port? p)
(raise-type-error 'display-list "output-port" p))
(do-lines->port l p newline)))

View File

@ -0,0 +1,24 @@
#lang scheme/base
(provide port->x-lines
check-mode
do-lines->port)
(define (check-mode who mode)
(unless (memq mode '(linefeed return return-linefeed any any-one))
(raise-type-error who "'linefeed, 'return, 'return-linefeed, 'any, or 'any-one" mode)))
(define (port->x-lines who p mode read-line)
(unless (input-port? p)
(raise-type-error who "input-port" p))
(check-mode who mode)
(let loop ([l null])
(let ([line (read-line p mode)])
(if (eof-object? line)
(reverse l)
(loop (cons line l))))))
(define (do-lines->port l p newline)
(for ([i (in-list l)])
(display i p)
(display newline p)))

View File

@ -536,6 +536,82 @@ module path.}
@note-lib[scheme/file] @note-lib[scheme/file]
@defproc[(file->string [path path-string?]
[#:mode mode-flag (or/c 'binary 'text) 'binary])
string?]{
Reads all characters from @scheme[path] and returns them as a string.
The @scheme[mode-flag] argument is the same as for
@scheme[open-input-file].}
@defproc[(file->bytes [path path-string?]
[#:mode mode-flag (or/c 'binary 'text) 'binary])
bytes?]{
Reads all characters from @scheme[path] and returns them as a
@tech{byte string}. The @scheme[mode-flag] argument is the same as
for @scheme[open-input-file].}
@defproc[(file->lines [path path-string?]
[#:mode mode-flag (or/c 'binary 'text) 'binary]
[#:line-mode line-mode (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) 'any])
bytes?]{
Read all characters from @scheme[path], breaking them into lines. The
@scheme[line-mode] argument is the same as the second argument to
@scheme[read-line], but the default is @scheme['any] instead of
@scheme['linefeed]. The @scheme[mode-flag] argument is the same as for
@scheme[open-input-file].}
@defproc[(file->value [path path-string?]
[#:mode mode-flag (or/c 'binary 'text) 'binary])
bytes?]{
Reads a single S-expression from @scheme[path] using @scheme[read].
The @scheme[mode-flag] argument is the same as for
@scheme[open-input-file].}
@defproc[(file->bytes-lines [path path-string?]
[#:mode mode-flag (or/c 'binary 'text) 'binary]
[#:line-mode line-mode (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) 'any])
bytes?]{
Like @scheme[file->lines], but reading bytes and collecting them into
lines like @scheme[read-bytes-line].}
@defproc[(display-to-file [v any/c]
[path path-string?]
[#:mode mode-flag (or/c 'binary 'text) 'binary]
[#:exists exists-flag (or/c 'error 'append 'update
'replace 'truncate 'truncate/replace) 'error])
void?]{
Uses @scheme[display] to print each @scheme[v] to @scheme[path]. The @scheme[mode-flag] and
@scheme[exists-flag] arguments are the same as for
@scheme[open-output-file].}
@defproc[(write-to-file [v any/c]
[path path-string?]
[#:mode mode-flag (or/c 'binary 'text) 'binary]
[#:exists exists-flag (or/c 'error 'append 'update
'replace 'truncate 'truncate/replace) 'error])
void?]{
Like @scheme[display-to-file], but using @scheme[write] instead of @scheme[display].}
@defproc[(display-list-to-file [lst list?]
[path path-string?]
[#:separator separator any/c #"\n"]
[#:mode mode-flag (or/c 'binary 'text) 'binary]
[#:exists exists-flag (or/c 'error 'append 'update
'replace 'truncate 'truncate/replace) 'error])
void?]{
Displays each element of @scheme[lst] to @scheme[path], adding
@scheme[separator] after each element. The @scheme[mode-flag] and
@scheme[exists-flag] arguments are the same as for
@scheme[open-output-file].}
@defproc[(copy-directory/files [src path-string?][dest path-string?]) @defproc[(copy-directory/files [src path-string?][dest path-string?])
void?]{ void?]{

View File

@ -2,12 +2,48 @@
@(require "mz.ss" @(require "mz.ss"
(for-label scheme/port)) (for-label scheme/port))
@title{More Port Constructors and Events} @title[#:tag "port-lib"]{More Port Constructors, Procedures, and Events}
@note-lib-only[scheme/port] @note-lib-only[scheme/port]
@; ---------------------------------------------------------------------- @; ----------------------------------------------------------------------
@section{Port String and List Conversions}
@defproc[(port->string [in input-port? (current-input-port)]) string?]{
Reads all characters from @scheme[in] and returns them as a string.}
@defproc[(port->bytes [in input-port? (current-input-port)]) bytes?]{
Reads all bytes from @scheme[in] and returns them as a @tech{byte string}.}
@defproc[(port->lines [in input-port? (current-input-port)]
[#:line-mode line-mode (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) 'any])
(listof string?)]{
Read all characters from @scheme[in], breaking them into lines. The
@scheme[line-mode] argument is the same as the second argument to
@scheme[read-line], but the default is @scheme['any] instead of
@scheme['linefeed].}
@defproc[(port->bytes-lines [in input-port? (current-input-port)]
[#:line-mode line-mode (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) 'any])
(listof bytes?)]{
Like @scheme[port->lines], but reading bytes and collecting them into
lines like @scheme[read-bytes-line].}
@defproc[(display-list [lst list?]
[out output-port? (current-output-port)]
[#:separator separator any/c #"\n"])
void?]{
Use @scheme[display] to each each element of @scheme[lst] to @scheme[out], adding
@scheme[separator] after each element.}
@; ----------------------------------------------------------------------
@section{Creating Ports} @section{Creating Ports}
@defproc[(input-port-append [close-at-eof? any/c][in input-port?] ...) input-port?]{ @defproc[(input-port-append [close-at-eof? any/c][in input-port?] ...) input-port?]{

View File

@ -3,10 +3,56 @@
(Section 'file) (Section 'file)
(require mzlib/file (require scheme/file
mzlib/process mzlib/process
mzlib/list) mzlib/list)
(define tmp-name "tmp0-filelib")
(when (file-exists? tmp-name) (delete-file tmp-name))
(display-list-to-file '("a" "b" "c") tmp-name #:separator #"\r\n" #:mode 'binary)
(test "a\r\nb\r\nc\r\n" file->string tmp-name #:mode 'binary)
(test #"a\r\nb\r\nc\r\n" file->bytes tmp-name)
(test '("a" "b" "c") file->lines tmp-name)
(test '(#"a" #"b" #"c") file->bytes-lines tmp-name)
(test '("a" "b" "c") file->lines tmp-name #:line-mode 'any #:mode 'binary)
(test '(#"a" #"b" #"c") file->bytes-lines tmp-name #:line-mode 'any #:mode 'text)
(err/rt-test (display-to-file #"a\nb" tmp-name) exn:fail:filesystem:exists?)
(display-to-file #"a\nb" tmp-name #:exists 'truncate)
(test #"a\nb" file->bytes tmp-name)
(display-to-file "\u03BB" tmp-name #:exists 'truncate)
(test #"\316\273" file->bytes tmp-name)
(write-to-file "\u03BB" tmp-name #:exists 'truncate)
(test #"\"\316\273\"" file->bytes tmp-name)
(test "\u03BB" file->value tmp-name)
(define tmp-name "tmp0-filelib")
(define-syntax-rule (err/rt-chk-test (op arg ...))
(err/rt-test (op arg ...) (check-msg 'op)))
(define (check-msg op)
(lambda (exn)
(regexp-match (format "^~a: " op) (exn-message exn))))
(err/rt-chk-test (file->string 'x))
(err/rt-chk-test (file->bytes 'x))
(err/rt-chk-test (file->string "x" #:mode 'other))
(err/rt-chk-test (file->bytes "x" #:mode 'other))
(err/rt-chk-test (file->value "x" #:mode 'other))
(err/rt-chk-test (display-list-to-file 10 "x"))
(err/rt-chk-test (display-list-to-file '(10) "x" #:mode 'other))
(err/rt-chk-test (display-list-to-file '(10) "x" #:exists 'other))
(err/rt-chk-test (file->lines "x" #:line-mode 'junk))
(err/rt-chk-test (file->lines "x" #:mode 'other))
(err/rt-chk-test (file->bytes-lines "x" #:line-mode 'junk))
(err/rt-chk-test (file->bytes-lines "x" #:mode 'other))
(err/rt-chk-test (display-to-file "y" "x" #:exists 'other))
(err/rt-chk-test (display-to-file "y" "x" #:mode 'other))
(err/rt-chk-test (write-to-file #"y" "x" #:exists 'other))
(err/rt-chk-test (write-to-file #"y" "x" #:mode 'other))
(err/rt-chk-test (display-list-to-file 'y "x"))
(err/rt-chk-test (display-list-to-file '(y) "x" #:exists 'other))
(err/rt-chk-test (display-list-to-file '(y) "x" #:mode 'other))
;; ----------------------------------------
(parameterize ([current-directory (current-load-relative-directory)]) (parameterize ([current-directory (current-load-relative-directory)])
(let ([rel (find-files values)] (let ([rel (find-files values)]
[abs (find-files values (current-directory))]) [abs (find-files values (current-directory))])

View File

@ -5,7 +5,31 @@
(define SLEEP-TIME 0.1) (define SLEEP-TIME 0.1)
(require mzlib/port) (require scheme/port)
;; ----------------------------------------
(let* ([p (lambda () (open-input-string "hello\r\nthere"))])
(test "hello\r\nthere" port->string (p))
(test #"hello\r\nthere" port->bytes (p))
(test '("hello" "there") port->lines (p))
(test '(#"hello" #"there") port->bytes-lines (p))
(test '("hello\r" "there") port->lines (p) #:line-mode 'linefeed)
(test '(#"hello\r" #"there") port->bytes-lines (p) #:line-mode 'linefeed)
(test '("hello" "" "there") port->lines (p) #:line-mode 'any-one)
(test '(#"hello" #"" #"there") port->bytes-lines (p) #:line-mode 'any-one))
(let* ([x (make-string 50000 #\x)]
[p (lambda () (open-input-string x))])
(test (string-length x) 'long-string (string-length (port->string (p))))
(test (string-length x) 'long-string (bytes-length (port->bytes (p)))))
(let ([p (open-output-bytes)])
(display-list '(1 2 3) p)
(test "1\n2\n3\n" get-output-string p))
(let ([p (open-output-bytes)])
(display-list '(1 2 3) p #:separator #"!!")
(test "1!!2!!3!!" get-output-string p))
;; ---------------------------------------- ;; ----------------------------------------

View File

@ -1,5 +1,10 @@
Version 4.1.2.4 Version 4.1.2.4
Added call-with-immediate-continuation-mark Added call-with-immediate-continuation-mark
In scheme/port: added port->string, port->bytes, port->lines
port->bytes-lines, and display-list
In scheme/file: added file->string, file->bytes, file->lines,
file->bytes-lines, write-to-file, display-to-file,
and display-list-to-file
Version 4.1.2.3 Version 4.1.2.3
Added variable-reference? and empty #%variable-reference form Added variable-reference? and empty #%variable-reference form