Read stdin to string in Newlisp - newlisp

How do you read the entire contents of standard input into a string in Newlisp? (i.e the entire remaining contents after the current read position - this operation is commonly called "slurp file")

You can use this:
(define (read-all)
(let (r "" ch "")
(while (setf ch (read-char))
(setf r (append r (char ch))))
r))
See also: http://www.newlisp.org/downloads/newlisp_manual.html#read-char

Related

Function returns list but prints out NIL in LISP

I'm reading a file char by char and constructing a list which is consist of list of letters of words. I did that but when it comes to testing it prints out NIL. Also outside of test function when i print out list, it prints nicely. What is the problem here? Is there any other meaning of LET keyword?
This is my read fucntion:
(defun read-and-parse (filename)
(with-open-file (s filename)
(let (words)
(let (letter)
(loop for c = (read-char s nil)
while c
do(when (char/= c #\Space)
(if (char/= c #\Newline) (push c letter)))
do(when (or (char= c #\Space) (char= c #\Newline) )
(push (reverse letter) words)
(setf letter '())))
(reverse words)
))))
This is test function:
(defun test_on_test_data ()
(let (doc (read-and-parse "document2.txt"))
(print doc)
))
This is input text:
hello
this is a test
You're not using let properly. The syntax is:
(let ((var1 val1)
(var2 val2)
...)
body)
If the initial value of the variable is NIL, you can abbreviate (varN nil) as just varN.
You wrote:
(let (doc
(read-and-parse "document2.txt"))
(print doc))
Based on the above, this is using the abbreviation, and it's equivalent to:
(let ((doc nil)
(read-and-parse "document2.txt"))
(print doc))
Now you can see that this binds doc to NIL, and binds the variable read-and-parse to "document2.txt". It never calls the function. The correct syntax is:
(let ((doc (read-and-parse "document2.txt")))
(print doc))
Barmar's answer is the right one. For interest, here is a version of read-and-parse which makes possibly-more-idiomatic use of loop, and also abstracts out the 'is the character white' decision since this is something which is really not usefully possible in portable CL as the standard character repertoire is absurdly poor (there's no tab for instance!). I'm sure there is some library available via Quicklisp which deals with this better than the below.
I think this is fairly readable: there's an outer loop which collects words, and an inner loop which collects characters into a word, skipping over whitespace until it finds the next word. Both use loop's collect feature to collect lists forwards. On the other hand, I feel kind of bad every time I use loop (I know there are alternatives).
By default this collects the words as lists of characters: if you tell it to it will collect them as strings.
(defun char-white-p (c)
;; Is a character white? The fallback for this is horrid, since
;; tab &c are not a standard characters. There must be a portability
;; library with a function which does this.
#+LispWorks (lw:whitespace-char-p c)
#+CCL (ccl:whitespacep c) ;?
#-(or LispWorks CCL)
(member char (load-time-value
(mapcan (lambda (n)
(let ((c (name-char n)))
(and c (list c))))
'("Space" "Newline" "Page" "Tab" "Return" "Linefeed"
;; and I am not sure about the following, but, well
"Backspace" "Rubout")))))
(defun read-and-parse (filename &key (as-strings nil))
"Parse a file into a list of words, splitting on whitespace.
By default the words are returned as lists of characters. If
AS-STRINGS is T then they are coerced to strings"
(with-open-file (s filename)
(loop for maybe-word = (loop with collecting = nil
for c = (read-char s nil)
;; carry on until we hit EOF, or we
;; hit whitespace while collecting a
;; word
until (or (not c) ;EOF
(and collecting (char-white-p c)))
;; if we're not collecting and we see
;; a non-white character, then we're
;; now collecting
when (and (not collecting) (not (char-white-p c)))
do (setf collecting t)
when collecting
collect c)
while (not (null maybe-word))
collect (if as-strings
(coerce maybe-word 'string)
maybe-word))))

Reading file to list of lists using Common Lisp

I need to read a text file using Common Lisp. File must be read to list of lists. Each list in the list must consist of a line from file.
Now, I have done such code:
(with-open-file (in file)
(loop for line = (read-line in nil nil)
while line
collect (coerce line 'list)))
But, for example, the rusult looks as: ((#\0 #\0 #\0) (#\1 #\0 #\1)). But I need to have result without #\ characters: ((0 0 0) (1 0 1)). How to fix it?
You are already converting the line to a list of characters;
all you need to do is convert the characters to numbers:
(with-open-file (in file)
(loop for line = (read-line in nil nil)
while line
collect (map 'list #'digit-char-p line)))
You can also use (parse-integer (string c)) instead of digit-char-p, but that seems an overkill.

return a line of text if match found

I am having some trouble working out how to return a line of text if a match is found.
(set 'wireshark "http://anonsvn.wireshark.org/wireshark/trunk/manuf")
(set 'arptable (map (fn (x) (parse x " ")) (exec "arp -a")))
(define (cleanIPaddress x)
(slice x 1 -1))
(define (cleanMACaddress x)
(upper-case (join (slice (parse x ":") 0 3) ":")))
(define (addIPandMACaddress x)
(list (cleanIPaddress (nth 1 x)) (cleanMACaddress (nth 3 x))))
(set 'arplist (map addIPandMACaddress arptable))
(set 'routerMAC (last (assoc (exec "ipconfig getoption en1 router") arplist)))
(find-all routerMAC (get-url wireshark))
returns
("20:AA:4B")
so I know that the code "works"
but I would like to retrieve the full line of text
"20:AA:4B Cisco-Li # Cisco-Linksys, LLC"
This can be performed simply by using a string-split procedure that allows us to use remove-if (the Common Lisp version of filter) to search through a string split by newlines removing any lines that do not contain the string we are searching for. That would result in a list of every line containing the string. The functions we will define here are already available via various Common Lisp libraries, but for the education purposes, we will define them all ourselves. The code you need works like so:
; First we need a function to split a string by character
(defun string-split (split-string string)
(loop with l = (length split-string)
for n = 0 then (+ pos l)
for pos = (search split-string string :start2 n)
if pos collect (subseq string n pos)
else collect (subseq string n)
while pos))
; Now we will make a function based on string-split to split by newlines
(defun newline-split (string)
(string-split "
" string))
; Finally, we go through our text searching for lines that match our string.
; Make sure to replace 'needle' with the string you wish to search for.
(remove-if #'(lambda (x)
(equal 'nil (search (string-upcase "needle")
(string-upcase x))))
(newline-split haystack))
You should be able to apply this strategy to the code you posted with a few small modifications. This code was tested on SBCL 1.0.55.0-abb03f9, an implementation of ANSI Common Lisp, on Mac OS X 10.7.5.
In the end I used:
(find-all (string routerMAC ".*") (get-url wireshark))

Convert char to number

I'm in the process of reading a flat file - to use the characters read I want to convert them into numbers. I wrote a little function that converts a string to a vector:
(defun string-to-vec (strng)
(setf strng (remove #\Space strng))
(let ((vec (make-array (length strng))))
(dotimes (i (length strng) vec)
(setf (svref vec i) (char strng i)))))
However this returns a vector with character entries. Short of using char-code to convert unit number chars to numbers in a function, is there a simple way to read numbers as numbers from a file?
In addition to Rainer's answer, let me mention read-from-string (note that Rainer's code is more efficient than repeated application of read-from-string because it only creates a stream once) and parse-integer (alas, there is no parse-float).
Note that if you are reading a CSV file, you should probably use an off-the-shelf library instead of writing your own.
Above is shorter:
? (map 'vector #'identity (remove #\Space "123"))
#(#\1 #\2 #\3)
You can convert a string:
(defun string-to-vector-of-numbers (string)
(coerce
(with-input-from-string (s string)
(loop with end = '#:end
for n = (read s nil end)
until (eql n end)
unless (numberp n) do (error "Input ~a is not a number." n)
collect n))
'vector))
But it would be easier to read the numbers directly form the file. Use READ, which can read numbers.
Note that read-like functions are affected by reader macros.
Pick an example:
* (defvar *foo* 'bar)
*FOO*
* (read-from-string "#.(setq *foo* 'baz)")
BAZ
19
* *foo*
BAZ
As you can see read-from-string can implicitly set a variable. You can disable the #. reader macro by setting *read-eval* to nil but anyway if you have only integers on the input then consider using parse-integer instead.

Loop Though Strings

I seem to be stuck trying to loop through strings to find characters that are not in the other string. The goal of the program is to loop though string one and document the characters that are not in the other string. The characters that are not in the other string will be printed out after all the checking is finished. They may not be repeated, hence I attempt to use three loops.
I am trying to debug the code below, since I have to eventually check both strings against each other, and I want to do this manually for the know how.
CG-USER(258): (defun stringprod (string1 string2)
(let ((newString nil))
(let ((letterSearchOn nil))
(loop for i from 0 below (length string1)
always
(setf (letterSearchOn (char string1 i))
(loop for j from 0 below (length string2)
(for ch = (char string2 j)
(/when (find ch letterSearchOn :test #'equal)
(append newString ch)))))))))
STRINGPROD
CG-USER(260): (stringprod "abc" "abc")
Error: (FOR CH = (CHAR STRING2 J)
(/WHEN (FIND CH LETTERSEARCHON :TEST #'EQUAL)
(APPEND NEWSTRING CH))) found where LOOP keyword expected.
Current LOOP context: FOR J FROM 0 BELOW (LENGTH STRING2)
(FOR CH = (CHAR STRING2 J)
(/WHEN (FIND CH LETTERSEARCHON :TEST #'EQUAL) (APPEND NEWSTRING CH))).
[condition type: PROGRAM-ERROR]
CG-USER(261):
How about something like this?
(defun remove-unsafe (str unsafe)
(remove-duplicates
(remove-if #'(lambda (c) (find c unsafe)) str)))
You need to check the syntax of LOOP: http://www.lispworks.com/documentation/HyperSpec/Body/m_loop.htm
(loop for i from start1
for j from start2
do ...)
Your code has added parentheses, missing do words and weird characters like /.
If your question is homework, you should tag it as such. If not you can use SET-DIFFERENCE.
(set-difference (coerce "abc" 'list) (coerce "bcd" 'list))
-> (#\a)