I am trying to implement a stream->file function that takes a stream of characters and prints it to a file. I feel I am close to a solution, but cannot figure out how to complete this.
(define stream->file
(lambda (filename str)
(let ((output (open-output-file filename)))
(letrec
((build-output-stream
(lambda (str)
(let ((char (write-char (stream-first str) output)))
(if (eof-object? char)
(begin
(close-output-port output)
empty-stream)
(stream-cons char (build-output-stream (stream-rest str))))))))
(build-output-stream str)))))
This doesn't do anything other than say #<stream> in the output. It creates a file, but does not write to it. What am I missing?
I find your code overly complicated. You need to write each element in the stream and since this is pure side effect you might as well use stream-for-each. Instead of doing the file port handling it's easier to use with-output-to-file since it will close the port when the thunk is done. This is the result:
(define (stream->file file-path stream)
(with-output-to-file file-path
(thunk (stream-for-each display stream))))
(stream->file "test-stream.txt" (stream #\t #\e #\s #\t #\newline))
; undefined, makes a file named "test-stream.txt" with the content "test\n"
Related
If Guile is not the best Scheme for this usage, then which one should I be looking at? I'm basically looking for a Guile equivalent of awk '{print $N}'. If Scheme can't do this, then I'd like to know why not.
Guile changed its I/O a bit between 2.0 and 2.2, so this uses r6rs I/O which (hopefully) works the same in both, but I haven't tested with 2.2.
This can be optimized further.
#!/usr/bin/guile \
-e start -s
!#
(use-modules (rnrs io ports))
;;; Reads one line from current-input-port and prints the field indicated by
;;; field-num. If the line does not have enough fields, it prints a newline.
;;; Returns the field, an empty string, or #f if end of file is reached.
(define (get-field field-num)
(let ((line (get-line (current-input-port))))
(if (eof-object? line)
#f
(let ((fields (string-tokenize line)))
(if (< field-num (length fields))
(let ((field (list-ref fields field-num)))
(put-string (current-output-port)
(string-append field "\n"))
field)
(and (put-string (current-output-port) "\n")
""))))))
;;; Repeat get-field until until end of file is reached
(define (get-all-fields field-num)
(if (get-field field-num)
(get-all-fields field-num)
#f))
(define (start args)
(if (and (> (length args) 1)
(integer? (string->number (list-ref args 1))))
(get-all-fields (1- (string->number (list-ref args 1))))
(display (string-join
`("Usage:" ,(list-ref args 0) "<field-number>\n")
" "))))
At my blog I have an essay giving a set of functions that make it easy to handle delimited text files.
I am attempting to read and write a matrix from file "data.txt".
The matrix is lists with strings inside of them.
When I am writing I want to write from the begining an override the data. Basically I delete the file every time. I need bether solusion for this.
May main problem is that after a couple readings and writhings of the file corrupts.
system error: Access is denied.; errno=5
My code:
;reading file returning matix of strings
(define (file-reader file-name)
(define pointer (open-input-file file-name))
(define (helper line)
(cond
((equal? line eof) '())
((cons (list line) (helper (read-line pointer))))))
(list-matr (helper (read-line pointer)))
)
;converting matrix of string to matrix of lists with strings inside
(define (list-matr str-matr)
(define (helper str-matr line-num)
(cond
((null? str-matr) '())
((= line-num 1) (cons (map (lambda (x) (string-append x "?")) (string-split (caar str-matr) "? ")) (helper (cdr str-matr) (+ line-num 1))))
((cons (string-split (caar str-matr) " ") (helper (cdr str-matr) (+ line-num 1))))))
(helper str-matr 1))
;saving in file
(define (writer file-name questions answers)
(cond
((file-exists? file-name) (delete-file file-name)))
(write-to-file file-name (string-append (string-join questions) "\n"))
(define (helper cur-l ans)
(cond
((null? ans))
((helper (write-to-file file-name (string-append (string-join (car ans)) "\n")) (cdr ans)))))
(helper '() answers)
)
(define (write-to-file path string)
(call-with-output-file path #:exists 'append
(lambda (newline)
(display string newline))))
Commands for calling the functions.
(file-reader "data.txt")
(writer "data.txt" questions answers)
I think the problem coming from that I don't close the files, but I can't figure out where to put the command for that.
If my code is very bad you can give me other examples for reading and writing matrix from file.
Thank you.
You are correct that the file will corrupt - it's never properly closed.
Without overwriting the file each time, you will need something outside of the normal R5RS/R7RS-small specification, and I'm not aware off the top of my head of any (final) SRFI that allows random file access. That said, many/most Scheme implementations provide some form of low-level I/O interface. The disadvantage of such is that you will have to track the structure very carefully so as to overwrite or add only the correct amount, which will probably be more work than rewriting the entire file.
I would recommend restructuring this completely. First, the call-with-output-file/with-output-to-file procedures will automatically overwrite the output file unless flagged otherwise (in most implementations - though the specifications state that the behaviour is undefined). They will also automatically close the file upon completion. Similar behaviour for the call-with-input-file/with-input-from-file procedures.
You can probably simplify everything by something like the following:
; reader
; this could be further simplified by replacing the cons call with
; (cons (<parse-procedure> l) r), to parse the input at the same time
(define (matrix-read filename)
(with-input-from-file filename (lambda ()
(let loop ((l (read-line))
(r '()))
(if (eof-object? l)
(reverse r)
(loop (read-line) (cons l r))))))
; I don't understand the input/output format...
; writer
(define (matrix-write filename data)
(with-output-to-file filename (lambda ()
(for-each
(lambda (l)
; again, I don't know the actual structure outside of a list
(display l)
(newline))
data))))
If you explain the input format, I can modify the answer.
I am having a problem implementing stream-map for the purpose of removing newlines from a stream of characters.
Below is my current implementation of remove-newlines:
(define remove-newlines2
(lambda (str)
(cond
((stream-empty? str) '())
(else (stream-map (lambda (x)
(cond
((equal? x #\newline) (remove x str ))
(else '())
)) ;procedure
str ; stream
)))))
I've toyed around with different implementations. However, it appears that no matter what I do, Racket only recognizes it as a stream, and does nothing further:
(remove-newlines2 (file->stream "text-source-file"))
#<stream>
>
Is there something simple I'm missing here?
It's the expected behavior, because stream-map returns a stream as its output. To get its contents you have to evaluate or force the result, for instance using stream->list on the stream:
(stream->list (remove-newlines2 (file->stream "text-source-file")))
I've got
(define (compiler exp)
(define (printer line) (display line) (newline))
(init-generators)
(let ((res (compile (append bootstrap (list exp)) function-res '())))
(map printer c-string-list)
(display bootstrap-c-code)
(add-c-function 'startup '(env) res)
(map (lambda (function) (map printer function) (newline)) c-function-list)
'ok))
and I need
(define (compile file-name)
(compiler (load file-name)))
But it doesn't work this way. It's executing directly. How can I load it as expression? (exp)
Are you looking for the read function, as opposed to load? Without knowing more about the implementation you're using, I can only point to the read documentation from r5rs. Use open-input-file to get a port from the filename, and then read from that port.
What's the content of the file file-name? If it's an expression, load will execute it, for instance if file-name contains (+ 1 1), (load filename) will return 2.
On the other hand, if file-name contains a list of symbols, they will be read as they are, for example if file-name contains '(+ 1 1) (notice the quote at the beginning), then (load filename) will return (+ 1 1), which I'm guessing is what you mean by saying that you need to load it as an expression.
If you need to load several expressions inside the file, surround them with a quote and a begin in the file:
'(begin
<your code here>
)
What's the best way to read input from stdin in racket?
In particular I'd like something like cin from c++ or scanf from c where I specify the types of things I want read and they are returned.
read-line is easy. To be portable across Unix and Windows, additional option is required.
(read-line (current-input-port) 'any)
Return and linefeed characters are detected after the conversions that
are automatically performed when reading a file in text mode. For
example, reading a file in text mode on Windows automatically changes
return-linefeed combinations to a linefeed. Thus, when a file is
opened in text mode, 'linefeed is usually the appropriate read-line
mode.
So, 'any is required to be portable when the input port is not a file (standard input).
Test program:
#lang racket
(let loop ()
(display "Input: ")
(define a (read-line (current-input-port) 'any))
(printf "input: ~a, length: ~a, last character: ~a\n"
a
(string-length a)
(char->integer (string-ref a (- (string-length a) 1))))
(loop))
In Windows, replace (read-line (current-input-port) 'any) with (read-line) and see what happens.
You can do pretty much everything you want to... at the low level, I would suggest (read-line) and (read-bytes). For higher-level processing (as e.g. scanf does), I would suggest a regexp-match on the input. For instance
(regexp-match #px" *([0-9]+)" (current-input-port))
I'd use the read procedure for the general case. If the data type to be read is known beforehand, use read-char, read-string, read-bytes.
Also, take a look at this implemenation for reading formatted input - a scanf in Scheme.
Here's a basis for line-by-line processing in Racket Scheme. It doesn't split an input line into multiple words, or do typed input, but this seems like a good place to put it.
(define (get)
(read-line (current-input-port)))
(define (put . vs)
(for-each display vs)
(displayln ""))
(define (sed fn)
(let ((line (get)))
(if (not (eof-object? line))
(begin
(fn line)
(sed fn))
'true)))
(sed (lambda (line)
(put "Hello, " line)))
Here's one that does split input, also encodes CSV for good measure.
(define (get)
(read-line (current-input-port)))
(define split string-split)
(define sep ",")
(define enc identity)
(define (enc-csv s)
(string-append "\"" (string-replace s "\"" "\"\"") "\""))
(define enc enc-csv)
(define (put . vs)
(displayln (string-join (map enc vs) sep)))
(define (sed fn)
(let ((line (get)))
(if (not (eof-object? line))
(begin
(fn line)
(sed fn))
'true)))
(sed (lambda (line)
(apply put (split line))))
This works in Racket. I'm not sure how much of it is specific to Racket. It doesn't seem to work in Chicken or Guile.