Jump to content

opening file (strings) into lisp list


Jef!

Recommended Posts

Good afternoon everyone!

 

I'm close to beeing done with my actual project that will fill out attributes of descriptions bubbles automatically depending on few querys.

 

I've built a list in a .txt file that contains all all types of material we use, with different grades looking like that for now

((Plate)(STEEL ASTM A572 GR.42)(STEEL CSA G40.21 300W (44W)))
((W-beam)(STEEL ASTM A992 or A572 GR.50)(STEEL CSA G40.21 350W))
((I-beam)(STEEL ASTM A36)(STEEL ASTM A36))

etc

I'm opened to reformating the list, as long as my non lisper collegues are able to edit if required without stepping into the lsp files. I also wanted my function to "autoupdate" if the file containing the grades is updated without having to make everyone restart autocad (since everytime the lisp reopen the file)

 

My intentions were to store its content into a lisp list (importedgradelist) to later on retrieve the informations regarding the grade with function like (nth y (nth x (importedgradelist)))

 

First attempt: I opened the file and used read-line

(defun C:RREAD ()
    (setq desc_grade_file (findfile "grades.txt")
          IMPORTEDGRADELIST NIL)
     (if (setq f (open desc_grade_file "r")) ;open the file in read mode 
      (while (setq txtLine(read-line f)) ;while lines of text exist in file
;        (ALERT txtLine) ;print the text into an alert. Works!
        (SETQ IMPORTEDGRADELIST (append IMPORTEDGRADELIST (list txtLine)))
      );close the while statement
      (princ "\n Error - Could not find file");print a message on the else statement
  )  
)   

as the items composing it are strings, I ended up with that

("((Plate)(STEEL ASTM A572 GR.42)(STEEL CSA G40.21 300W (44W)))" "((W-beam)(STEEL ASTM A992 or A572 GR.50)(STEEL CSA G40.21 350W))" "((I-beam)(STEEL ASTM A36)(STEEL ASTM A36))"..... and cant use it. (I think)

 

Then I thought the way to go would be to use the (setq txtchar(chr(read-char f))) function. I know the char retrieving works, the pain in my finger reminds me of it... Ive added (alert txtchar) and had to click on "ok" a billion times for each char on that list (Yeah, newbie mistake. Very funny one thought!) Heres the code I made

(defun C:READchar ()
    (setq desc_grade_file (findfile "grades.txt")
          IMPORTEDGRADELIST NIL)
     (if (setq f (open desc_grade_file "r")) ;open the file in read mode 
      (while (setq txtchar(chr(read-char f))) ;while lines of text exist in file
;        (ALERT txtchar) ;print char to an alert... works but don't. seriously ^±^' 
        (SETQ IMPORTEDGRADELIST (append IMPORTEDGRADELIST (txtchar)))
      );close the while statement
      (princ "\n Error - Could not find file");print a message on the else statement
  )  
)

Of course now I can't append since I dont have a list anymore. I'm kind of stuck here and not sure what to do next. I'm not even sure if I'm heading in the correct direction. I'd be glad to receive any comments, suggestions and/or advices!

 

Thanks and cheers,

Jef!

Link to comment
Share on other sites

I would take a different tack and only have a single line per entry but have it set up with known parameters. Something like this

1234567890123456789012345678901234567890

Newname my name Col Line Type

ceiling--2 ceiling--2 6 continuous

ceiling--3 ceiling--3 6 continuous

 

Then using the substr command you can read each individual variable without using lots of nth's

 

(setq oldname (substr ans 1 )
(setq newname (substr ans 10 )

((Plate STEEL ASTM A572 GR.42 STEEL CSA G40.21        300W 44W)
(W-beam STEEL ASTM A992 or A572 GR.50 STEEL CSA G40.21 350W )
(I-beam STEEL ASTM A36 STEEL ASTM                      A36))

Link to comment
Share on other sites

Thanks for sharing your thougths on that one

@Lee Mac: Special thank to you, for your site that is a great reference that helped me learn so much, thank you for your HUGE commitment toward many forums. That being said, despite the fact that I could find many tutorials for real-line and read-char functions, I didn't find much about the read function. From what i've seen, only the first expression is returned, so I don't see how it could suit my needs.

 

@Bigal: I think I see where your going, but with substr functions, if someone modify the list, the parameters for the substr would also have to be change accordingly.. and making the values dynamic would be complicated.

 

Yesterday I was trying to use append with strings, I think I was tired...

Ok, now I've manage to populate my lisp list with read-char and strcat and using autolisp string-handling functions (like vl-string-trim) seems the easiest way to complete the formating...

When I monitor !importedgradelist I can see that it gets populated but can't figure why nothing get executed after my while function. Did I misplaced the close?

(defun C:READchar ()
    (setq desc_grade_file (findfile "grades.txt")
          IMPORTEDGRADELIST ""); "" instead of nil to be able to strcat
     (if (setq f (open desc_grade_file "r"))
       (while (setq txtchar(chr(read-char f))) 
        (SETQ IMPORTEDGRADELIST (strcat IMPORTEDGRADELIST txtchar))
       );close the while statement
       (princ "\n Error - Could not find file");print a message on the else statement
     );close if
    (close desc_grade_file); -> is this close at the correct place? <-
    (ALERT IMPORTEDGRADELIST); ->This alert is not being processed..why?<-
    (setq IMPORTEDGRADELIST (vl-string-trim "\n" IMPORTEDGRADELIST)) 
)

I also tried to place the close file in a prong with the while, it didn't work either

        (if (setq f (open desc_grade_file "r")) ;open the file in read mode 
          (progn(
            (while (setq txtchar(chr(read-char f))) ;while lines of text exist in file
              (SETQ IMPORTEDGRADELIST (strcat IMPORTEDGRADELIST txtchar))
             );close the while statement
            (close desc_grade_file) ;still dont work?
           );close the progn
        (princ "\n Error - Could not find file");print a message on the else statement
       );close if
    
    (ALERT IMPORTEDGRADELIST); This alert is still not processed....

Link to comment
Share on other sites

@Lee Mac: Special thank to you, for your site that is a great reference that helped me learn so much, thank you for your HUGE commitment toward many forums.

 

Thank you Jef! -

I really appreciate your gratitude for the time & effort that I invest in my site and in my contributions across the various CAD forums.

 

That being said, despite the fact that I could find many tutorials for real-line and read-char functions, I didn't find much about the read function. From what i've seen, only the first expression is returned, so I don't see how it could suit my needs.

 

Here is the documentation for the read function:

 

http://exchange.autodesk.com/autocad/enu/online-help/browse#WS1a9193826455f5ff1a32d8d10ebc6b7ccc-6970.htm

 

For use in your program:

(defun c:readit ( / des lst str txt )
   (if
       (and
           (setq txt (findfile "grades.txt"))
           (setq des (open txt "r"))
       )
       (progn
           (while (setq str (read-line des))
               (setq lst (cons (read str) lst))
           )
           (close des)
           (print (reverse lst))
       )
       (princ "\ngrades.txt not found or could not be opened.")
   )
   (princ)
)

Link to comment
Share on other sites

Thanks for the link, this is one of the documentation that I read that made me say "From what i've seen, only the first expression is returned, so I don't see how it could suit my needs.", and reading your more-than-concrete example I realized that read-line or read-char also send the first line/char. This is why we use them nested in a while function. That is the kind of thing that I never get looking at Autodesk's not-so-concrete-examples.

 

 

So the close and the manipulation of the lst (print (reverse lst)) are in the progn. Understood. 3 questions tho

-if your var lst is local, how can you use its content when the execution is completed?

-(not the first time i've seen it but don'T understand) why do you reverse the list?

I always put and use alerts (like everywhere) when I'm creating lisps to follow the execution and also to monitor the value of variables.. So i'm very curious to know: Why if I substitute (print (reverse lst)) for (alert (reverse lst)), I don't get any popup alert executing the lisp?

edit: If I remove lst from local variables (and make it global), execute the command readit it then look at it (!lst) I get nil as a result...

 

Again, many thanks!

Link to comment
Share on other sites

So the close and the manipulation of the lst (print (reverse lst)) are in the progn. Understood.

 

Yes, since these expressions are to be evaluated as part of the 'then' argument for the if function.

 

Since the if function can only accept three parameters: the test expression, the 'then' expression and the 'else' expression, in order to evaluate multiple expressions for the 'then' parameter, I pass the expressions to the progn function (which simply evaluates all supplied expressions and returns the value of the last evaluated expression) so that this single progn expression can be passed to the if function to constitute the 'then' argument.

 

-if your var lst is local, how can you use its content when the execution is completed?

 

This was only an example, the program could be written as a function to return the list data for use by the calling program, e.g.:

(defun readit ( txt / des lst str )
   (if
       (and
           (setq txt (findfile txt))
           (setq des (open txt "r"))
       )
       (progn
           (while (setq str (read-line des))
               (setq lst (cons (read str) lst))
           )
           (close des)
           (reverse lst)
       )
   )
)

You would then call the above with the filename of the text file:

(readit "c:\\grades.txt")

-(not the first time i've seen it but don't understand) why do you reverse the list?

 

Consider how a list is constructed using the cons function:

_$ (foreach x '(1 2 3 4 5) (setq l (cons x l)))
(5 4 3 2 1)

The cons/reverse combination is much faster than using append.

 

I always put and use alerts (like everywhere) when I'm creating lisps to follow the execution and also to monitor the value of variables.. So i'm very curious to know: Why if I substitute (print (reverse lst)) for (alert (reverse lst)), I don't get any popup alert executing the lisp?

print / princ / prin1 can accept any AutoLISP data type, alert only accepts strings.

The return of (reverse lst) is a list.

Link to comment
Share on other sites

Just a bit more on a 1 liner you are right setting up a column arrangement is a bit of a pain, an alternative is to use a CSV comma seperated file, you make a search for comma defun by reading a line then stepping 1 character a time till you find the "," it then just require setting a variable and calling the defun, nice thing is once done you can use it again no matter how many variables as you know the maximum No of characters.

 

 ; example not tested
(setq x 1)
(setq var1 (csvstr newline x)) ; 1 is the 1st character in the readline 
(setq var2 (csvstr newline x)) ; x is the number of the last character just found
(setq var3 (csvstr newline x)) ; just repeat  

(defun csvstr ( )
   (setq ans "")
   (setq char_found "")
   (while (/= x y)
     (setq char_found (substr new_line x 1))
     (setq x (+ x 1))
     (if (= char_found ",")
       (setq x y)
       (setq ans (strcat ans char_found))
     )
   )
)

Link to comment
Share on other sites

I totally understand the progn function, I was not sure if I had to put the close within the if. A Wise man wrote somewhere "With Mathematics there is the possibility of perfect rigour, so why settle for less?" The close function belong in the if and is required only if the file is opened. (So logic I am almost ashamed that I asked! lol) 100% understood. :)

 

 

_$ (foreach x '(1 2 3 4 5) (setq l (cons x l)))
(5 4 3 2 1)

The cons/reverse combination is much faster than using append.

Right! Cons combine a single element to a list puting it before the list. totally understood.

 

 

print / princ / prin1 can accept any AutoLISP data type, alert only accepts strings.

The return of (reverse lst) is a list.

Alert only accept strings! That was one piece missing in my puzzle! Even with that part I cant figure why in your first example (c:readit ) , after closing the file the "(print (reverse lst))" was not printing anything. I need to have a lisp var containing the list to use it, so I tried to replace the (print (reverse lst)) for (setq templst (reverse lst)) but unfortunately !templst returns nil. My brain is totally fried at the moment. /me almost sad :D

 

 

Even tho I must admit the way I did may not be the optimal and fastest one, (I did it with read-char and strcat), but I have gathered all the information required.

Command: !importedgradelist
"(((Plate)(STEEL ASTM A572 GR.42)(STEEL CSA G40.21 300W (44W)))((W-beam)(STEEL ASTM A992 or A572 GR.50)(STEEL CSA G40.21 350W))....

For now, I would settle for something that works... The only problem left in that direction is that I have it in a string and need to transfer it somehow in a list... haven't found how. Can it be done?

 

 

edit: I just had to reconnect and after refreshing saw Bigal's reply. Before beginning this task I thought about using cvs, and without digging further I wasn't sure if I could manage a list of lists in a csv. I'm starting to have an headache, I'll take a deeper look tomorrow in the morning.

Link to comment
Share on other sites

Even with that part I cant figure why in your first example (c:readit ) , after closing the file the "(print (reverse lst))" was not printing anything. I need to have a lisp var containing the list to use it, so I tried to replace the (print (reverse lst)) for (setq templst (reverse lst)) but unfortunately !templst returns nil.

 

Looking over the code for my original example, I see no reason why the code should fail, unless the file is empty. Though note that the scope of the lst variable is limited to the c:readit function, since it has been declared local to this function.

 

Rather than using a global variable 'templst', I would recommend using the return of the function as shown in my last post ('readit') in your main program. Hence, your main program would call the function:

(setq templst (readit "c:\\grades.txt"))

With the necessary error trapping to ensure that templst is non-nil.

 

Even tho I must admit the way I did may not be the optimal and fastest one, (I did it with read-char and strcat), but I have gathered all the information required.

Command: !importedgradelist
"(((Plate)(STEEL ASTM A572 GR.42)(STEEL CSA G40.21 300W (44W)))((W-beam)(STEEL ASTM A992 or A572 GR.50)(STEEL CSA G40.21 350W))....

For now, I would settle for something that works... The only problem left in that direction is that I have it in a string and need to transfer it somehow in a list... haven't found how. Can it be done?

 

Well yes - any set of multiple lists can be interpreted by the read function by first combining the lists into a single list expression, e.g.:

(read (strcat "(" importedgradelist ")"))

Though, be aware that points (periods) in variable symbols will be ignored by the read function, since the function will attempt to retrieve a numerical value or dotted list pair when encountering a point.

Link to comment
Share on other sites

Though, be aware that points (periods) in variable symbols will be ignored by the read function, since the function will attempt to retrieve a numerical value or dotted list pair when encountering a point.
Ok, i've made some new tests today. Since I had points in my grades.txt, I find&replaced every points just to be sure they were not the cause of my issue. I even removed all the "-", to keep only letters, digits and parenthèses. It still didn't work. I've put the readit and the retrieving function in the same lsp file (singletest.lsp) containing

(defun readit ( txt / des lst str )
   (if
       (and
           (setq txt (findfile txt))
           (setq des (open txt "r"))
       )
       (progn
           (while (setq str (read-line des))
               (setq lst (cons (read str) lst))
           )
           (close des)
           (reverse lst)
       )
   )
)
(defun c:test ()
(setq templst (readit "c:\\grades.txt"))
)

Command: (LOAD "D:/Users/Jef/Desktop/singletest.lsp") C:TEST

Command: TEST

nil

Command:

Command: !templst

nil

The file is not empty, and is in CADs support file search path list

Command: (findfile "grades.txt")
"\\\\server\\project\\_drawing dept\\jef\\script\\grades.txt"

I definitly got something tho. I tried as you said

(read (strcat "(" importedgradelist ")"))

and it returned nothing (!) but reacted otherwise when I used the same function with a string I created.

 

Command: !stringgg

"this is a string"

Command: (type stringgg)

STR

Command: (type importedgradelist)

STR

Command: (read (strcat "(" importedgradelist ")"))

Command: (read (strcat "(" stringgg ")"))

(THIS IS A STRING)

Definitly am onto something here... OMGGGGGG I just found the 2 problems.

1 - in my grades.txt I had a parentheses imbalance, 1 more opening than what I had closing. My baaaad, sooorry! Parentheses. If you don't like them, don't lisp! ok. still c:test didn't work..

 

2- (setq templst (readit "c:\\grades.txt")) seemed weird. Looking for it on my c: drive maybe? It is not, and the findfile does the job of finding it.. I tried (setq templst (readit "grades.txt")) and BAM! EVERYTHING worked!

 

-My car doesn't start, I've checked everything, battery, spark plugs, wires, air filter...

-It has gas?

-...

 

Now I only have to use lisp to put back the dots in my grades...piece of cake, maybe =D

thank you thank you thank you thank you thank you (inhaaaaaale) thank you thank you :)

Link to comment
Share on other sites

Glad you were able to solve it Jef! - well done :)

 

My earlier example using "c:\\grades.txt" as an example filepath was perhaps confusing. Since the function uses findfile, the filepath argument may be an absolute filepath (e.g. "c:\\grades.txt"), relative filepath (e.g. "..\\grades.txt") or just a filename (e.g. "grades.txt") for which the function will search the working directory and SFSP.

 

Nevertheless, I'm sure you've learnt a great deal from the experience!

 

Lee

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...