Author Topic: ( tutorial ) Reading a file using AutoLISP  (Read 12770 times)

0 Members and 1 Guest are viewing this topic.

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
( tutorial ) Reading a file using AutoLISP
« on: January 22, 2008, 04:25:41 PM »
Feel free to fix any errors you may see or add content.

Code: [Select]
;; reading ASCII files with AutoLISP

;;; Opening
;;
;; first you must 'open' a file and specifiy if you want to 'r' read it, 'w'
;; write to it or 'a' append it. In the file path your have a choice of using
;; either a single forward slash ( / ) or double backslashes ( \\ ).

;; (setq fo (open "c:\\temp\\afile.txt" "r"))

(setq fo (open "c:/temp/afile.txt" "r"))

;; which will return something like this
;; #<file "c:/temp/afile.txt">

;; think of 'fo' as a sort of link to the file you are going to read.

;; the contents of 'afile.txt'
;;
;; 1. line one
;; 2. line two
;; 3. line three
;; 4. line four
;; #  comment line followed by an empty line
;;
;; 5. line five
;;

;;; Reading
;;
;; now using other autolisp functions we can read the the contents of the file.
;; We have a choice of one character (read-char) or the whole line (read-line).
;; Let's look at the later first.
;;
;; We can store the first line of our fine in a variable using the 'read-line'
;; function.

(setq aline (read-line fo))

;; 'aline' now contains [ "1. line one" ] a string. From this point you can
;; parse the string anyway you like. To get the next line from our file we
;; simply issue the same statement again.

(setq aline (read-line fo))

;; 'aline' now contains [ "2. line two" ].

;; Once you're done with the file it's a good idea to 'close' it.

(close fo)

;; Reading a file one line at a time is not usually what we want to do. Most
;; of the time you will want to read each line of a file and examine it in a
;; loop. For that we will use the 'while' function.

(setq fo (open "c:/temp/afile.txt" "r"))

(while (setq aline (read-line fo))
       (princ (strcat aline "\n")
       )
(princ)

(close fo)

;; which will produce something like this.
;;
;; 1. line one
;; 2. line two
;; 3. line three
;; 4. line four
;; #  comment line followed by an empty line
;;
;; 5. line five

;; Once we reached the end of our file 'read-line' returned 'nil' which
;; stopped out 'while' loop.

;; Let us take it one step further and examine each line using the 'if' and
;; 'wcmatch' function to look for a certain string within each line of our
;; file.

(setq fo (open "c:/temp/afile.txt" "r"))

(while (setq aline (read-line fo))
       (if (wcmatch aline "*line t*")
         (princ (strcat "Found a match: " aline "\n"))
         ); if
       ); while
(princ)

(close fo)

;; which should return something like this.
;;
;; Found a match: 2. line two
;; Found a match: 3. line three

;; One more example and then we will move on. Remember that '#' we have in our
;; file? This time we will read the file but ignore that line when we come to
;; it along with any blank lines.

(setq fo (open "c:/temp/afile.txt" "r"))

(while (setq aline (read-line fo))
       (if (/= (substr aline 1 1) "#")
         (if (/= aline "")
           (princ (strcat aline "\n"))
           )
         )
       )
(princ)

(close fo)

;; which should return something like this.
;;
;; 1. line one
;; 2. line two
;; 3. line three
;; 4. line four
;; 5. line five

;; Now let us look at the 'read-char' function. 'read-char' works like
;; 'read-line' only it returns the ASCII character code (inetger) not a
;; string, for every character in our file. In order to get the string you
;; have to use the 'chr' function.

(setq fo (open "c:/temp/afile.txt" "r"))

(setq charcnt 0)

(while (read-char fo)
  (setq charcnt (1+ charcnt))
  )

(princ (strcat "I counted " (itoa charcnt) " characters\n"))

(princ)

(close fo)

;; that should return something like
;; "I counted 107 characters"

;; what about creating a list of character codes in our file.

(setq fo (open "c:/temp/afile.txt" "r"))

(while (setq achr (read-char fo))
  (setq chrlst (cons achr chrlst))
  )

(close fo)

;; that should produce something like this.
;; (10 101 118 105 102 32 101 110 105 108 32 46 .... )


TheSwamp.org  (serving the CAD community since 2003)

DanB

  • Bull Frog
  • Posts: 367
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #1 on: January 23, 2008, 12:12:31 PM »
I could of really used this 2-3 months ago, somehow I plowed through what I needed but this would of helped alot. Thanks for sharing, already copied it down for future reference (just in case).

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #2 on: January 25, 2008, 08:11:15 AM »
Great article Mark, very readable writing style. Perhaps an idea for the next in your I/O series could be one on error recovery, like "file not found", "file already opened / locked" etc.

:)
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #3 on: January 28, 2008, 12:33:03 PM »
Great article Mark, very readable writing style.
Thank ya sir.

Quote
Perhaps an idea for the next in your I/O series could be one on error recovery, like "file not found", "file already opened / locked" etc.
Good idea! I'll add that when I get a few minutes.
TheSwamp.org  (serving the CAD community since 2003)

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #4 on: February 12, 2008, 12:11:14 PM »
Thanks Mark for the tutorial.

I know some folks already have "File Read" routines and many would like one.
Mark has given us the basics of a working routine.
Perhaps some would be willing to share there routine?

My thought is that most times I want to read the entire file into a list.
This makes the process very quick but risks having too mush memory consumed.

The alternate is to read the file & abort the read on some condition.
This method is no problem if the routine is dedicated but to create a reusable routine
would require that the calling routine pass the conditional statement to the file reader routine.
This is a problem if the file data must be decoded before the "End Read" condition is evaluated.
So the options are to pass a chunk of code to be used for the evaluation or to pass a subroutine
"Name" so that each line read is evaluated & the return value T/nil will stop the Read Process.

So my ideal routine would:
1. Open the file for Reading, return nil if failed to open
2. Read the file, argument passed to signal "Read entire file" or "Conditional Stop"
3. Return the File contents or error message
4. Close the file

Well I'm though rambling. Anyone written such a routine? Or want to share what they use?
Or want to share there thoughts on Read Write File Data?
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #5 on: February 18, 2010, 10:58:05 AM »
moving right along ...
Code: [Select]
;;; Making sure the file exists
;;
;; findfile will return either TRUE or NIL; true if the file is found, NIL
;; otherwise. Knowing this we can use a simple 'if' statment to make sure
;; the file is found before we move on to reading or writing to the file.
;;

(setq fn "c:/temp/afile.txt")

(if (findfile fn)
  (alert "file was found") ;; TRUE
  (alert "file was NOT found") ;; NIL
  )

;; the next logical step is to wrap our 'open' statment within an 'if' statment.

(setq fn "c:/temp/afile.txt")

(if (findfile fn)
  (progn
    (setq fo (open fn "r"))
    (while (setq aline (read-line fo))
           (princ (strcat aline "\n"))
           );while
    (princ)
    (close fo)
    );progn
  (alert "file was NOT found")
  );if
« Last Edit: February 18, 2010, 11:26:31 AM by Mark »
TheSwamp.org  (serving the CAD community since 2003)

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #6 on: February 18, 2010, 11:03:54 AM »

Mark,

The (Close fo) will cause an error if FO has no value.
The statement would be better moved into the
(if (findfile fn) code block

or called thusly
(if fo (Close fo) )
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Lee Mac

  • Seagull
  • Posts: 12913
  • London, England
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #7 on: February 18, 2010, 11:19:18 AM »
Well I'm though rambling. Anyone written such a routine? Or want to share what they use?
Or want to share there thoughts on Read Write File Data?

Inspired by your idea:

Code: [Select]
(defun _read (file foo / ofile lst nl)

  (cond (  (setq ofile (open file "r"))
         
           (while (and (setq nl (read-line ofile))
                       (or (not foo)
                           ((eval foo) nl)))
             
             (setq lst (cons nl lst)))

           (close ofile)))
 
  (reverse lst))

(defun c:test (/ file)
  (if (setq file (getfiled "File" "" "txt" 8))

    (_read (findfile file) '(lambda (x) (wcmatch x "#")))))

I would usually use something like that to read files, but without the condition.

Michael's _readstream is also pretty useful.  8-)

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #8 on: February 18, 2010, 11:27:07 AM »

Mark,

The (Close fo) will cause an error if FO has no value.
The statement would be better moved into the
(if (findfile fn) code block
You're right, thanks.
TheSwamp.org  (serving the CAD community since 2003)

nivuahc

  • Guest
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #9 on: February 18, 2010, 11:29:18 AM »
I just wanted to mention that I love posts like these. Thanks for doing it!

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: ( tutorial ) Reading a file using AutoLISP
« Reply #10 on: February 18, 2010, 02:08:45 PM »
Typically I test the file pointer returned by the (open...) call - no point in closing a file that didn't open.  It also provides a handy nook for "why didn't that file get read" error handling.

Code: [Select]
(if
   (setq
      file_ptr
      (open "that file.txt" w)
   )
   (progn

   ; Do what you gotta here, (read-line, etc.)

      (close file_ptr)
   )
   (progn

      (alert "Hey, I can't do that!")

   )
)
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}