Jump to content

Utilizing AutoCAD's FIND feature in lisp routines?


Comatosis

Recommended Posts

Thanks for the info Irneb.
You're welcome! Though it's not good news :x

 

About the RegExp stuff, I agree fully with Lee. It's one of those tools all programmers should keep in their belt. Whenever you work with strings the chances are you can do whatever you need more efficiently, less typing, more robustly, etc. using RegExp. As a sample look at my CSV implementation here: http://www.cadtutor.net/forum/showthread.php?55092-Reading-from-an-external-file-in-a-LISP

 

Note however, that different environments' RegExp's are not always the same. Some work faster than others, while some have different codes. For another nice resource: http://www.regular-expressions.info/vbscript.html

Link to comment
Share on other sites

  • Replies 74
  • Created
  • Last Reply

Top Posters In This Topic

  • Comatosis

    27

  • Lee Mac

    19

  • BlackBox

    13

  • irneb

    10

Top Posters In This Topic

I hit another snag, but this one is more due to my stubbornness than anything :). Can I set up an ssget command to select all TEXT or MTEXT objects that contain certain strings of text, but that also contain a bunch of other text I don't care about?

 

What I had so far was this:

 


(setq mtlist (ssget "_X" '((0 . "TEXT,MTEXT") (3 . "##`.[~.]##")))) 

 

But it returns nil because (I assume) it's looking for content that consists only of those wildcards, returning nil after it realizes there's also other stuff in there. I could circumvent this step altogether by just having the user select the correct text objects beforehand, but I want to eliminate human interaction as much as possible. Is something like this doable? I set the content filter to just (3 . "*") as a test and it still returns nil (whereas removing the filter altogether indeed selects all text and mtext). Is this because it's looking for one-character-long strings?

 

Thanks.

Link to comment
Share on other sites

Untested, but maybe something like this?

 

(setq mtlist
   (ssget "_X"
      '(
           (0 . "TEXT,MTEXT")
           (-4 . "<OR")
           (1 . "*##`.[~`.]##*")
           (3 . "*##`.[~`.]##*")
           (-4 . "OR>")
       )
   )
)

Link to comment
Share on other sites

That completely worked, but why would this work and not what I had? (I also tried to enclose my search string with asterisks as a catchall, but to no avail). The way I understod your code, the OR is there to do what I wanted to do earlier, but to either (or both) code 1 and code 3 items as opposed to just code 3. I'm having a hard time translating the syntax into logic, I guess :(.

 

At any rate, thanks once again for coming through with the awesomeness.

Link to comment
Share on other sites

My filter is checking for a match in either DXF 1 or 3 (or both), since we can't guarantee that the string will be located in DXF 3, or that the entity will have a DXF 3 group at all.

 

Notice also the difference between the wildcard patterns, I added another literal inside your negative character set, and enclosed the pattern inside asterisks to include entities for which the pattern is midway through the string, and not just the string itself.

 

e.g.

 

(wcmatch "test" "es") = nil
(wcmatch "test" "*es*") = T

Link to comment
Share on other sites

My filter is checking for a match in either DXF 1 or 3 (or both), since we can't guarantee that the string will be located in DXF 3, or that the entity will have a DXF 3 group at all.

 

Notice also the difference between the wildcard patterns, I added another literal inside your negative character set, and enclosed the pattern inside asterisks to include entities for which the pattern is midway through the string, and not just the string itself.

 

e.g.

 

(wcmatch "test" "es") = nil
(wcmatch "test" "*es*") = T

 

The [~.] character is supposed to represent "alphanumeric characters only," but I based this off of what can be done in AutoCAD's Find. Is there something different I should be doing with lisp?

 

Also, I have no idea why this is, but the code you gave me worked on the first sheet I tried, but not on another one that also had just 1 big mtext block with these strings. I've closed down autocad to make sure I'm wiping all the variables clean and whan not, and it still doesn't wanna work on this other sheet. Weird.

Link to comment
Share on other sites

The [~.] character is supposed to represent "alphanumeric characters only," but I based this off of what can be done in AutoCAD's Find. Is there something different I should be doing with lisp?

 

I see - I thought it was meant to be any character excluding the period (.) character. Maybe use "[0-9a-zA-Z]" instead.

Link to comment
Share on other sites

I've hit yet another snag that is driving me crazy. Is there a reason why lisp would get hung up on lists that are length 0 or 1? I'm working with "lists of lists" if this makes any sense. (and yes, I'm aware that this is probably my fault and not lisps's 8))

 

Basically, this function receives a list of items, where each item contains both a string and an integer (not sure if autolisp interprets these as associated lists, considering my total noobness with lisp). Anyway, if the list is nil, the function should spit out a particular string. If the list has only one item, it works with the items in the list and spits out a concatenated string that incorporates these items. If the list has more than 1 item, same as before, but the string itself will say different things.

 

Now, running what I have, I can get multi-item lists to work and output the correct string, but if the list length is 0 or 1, for some reason it doesn't return the string. Some debugging leads me to believe that the function doesn't even receive the list in these cases ("error: bad element in arguments"), even though I have no idea why this would be given that all the lists were created by the same process.

 

Here's some pseudo-pseudocode:

 

(defun makestring ( lst / )
(setq str "")
  (cond      
      ( (= (length lst) 0) (setq str "STRING X"))          
      ( (= (length lst) 1) ((do stuff) (setq str "STRING Y")))  
      (T ((do other stuff) (setq str "STRING Z")))
  )
)        

 

My parenthesis pairing might be off in the above example, but that's just a result of transcribing/simplifying the code to show you guys.

 

I really have no idea what's going on. :(

Link to comment
Share on other sites

You would have to give an example of your list structure, or even how you are constructing the list. I cannot glean anything in particular from the code as it stands.

Link to comment
Share on other sites

After running a ! on the command prompt, here's what the function is supposed to receive. This is a copy/paste of the results as they appear on the command prompt, aside from having edited the string content for secrecy purposes :unsure::

 

!list1
(("string1" 2) ("string10" 1) ("string11" 1) ("string2" 2) ("string3" 1) 
("string4" 1) ("string5" 7) ("string6" 2) ("string7" 1) ("string8" 1) ("string9" 1))

!list2
(("string" 1))

!list3
nil

 

As for how these lists are produced, they receive string lists of this form:

 

("string1" "string1" "string2" "string3" "string4" "string2" "string5" "string5"

"string5" "string5" "string6" "string5" "string7" "string8" "string9" "string10"

"string6" "string11" "string5" "string5")

 

(the function below is there to pair up each string with the number of times it occurs on a sheet, both of which will vary from sheet to sheet, hence the nil case)

 

 

 
(defun makelist (lst / final)
  (setq sorted (vl-sort lst '>))   
  (setq tempk "")
  (setq templ 0)
  (setq final '())
  (while (> (length sorted) 0)
    (setq tempk (car sorted))    
    (setq templ (length sorted))
    (setq sorted (vl-remove (car sorted) sorted))
    (setq diff (- templ (length sorted)))  
    (setq final (cons (list tempk diff) final))   
  )       ;end while
) ; end makelist 

Edited by Comatosis
Link to comment
Share on other sites

I can't immediately see anything that looks like it would error, have you used the VLIDE to determine the exact line at which the code crashes? (not sure how?, see here)

 

You could condense your 'makelist' function to something like:

 

(defun makelist ( lst / out )
   (while lst
       (setq out (cons (list (car lst) (- (length lst) (length (setq lst (vl-remove (car lst) (cdr lst)))))) out))
   )
)

 

Although this does make it less readable... :ouch:

Link to comment
Share on other sites

Yeah, someone who's any decent at this would easily come up with routines half as long as mine are. I'm scared of posting all my code for that reason, heheh.

 

Anyway, I decided to do away with the conditional and just use multiple if statements (less parentheses to worry about, heh). Still no dice.

 

The function returns "MORE THAN ONE." correctly, but the other two cases still return nil, even though internally the function receives the list and updates str as it should. Argh. My debugging skills so far have been limited to using (getstring) as a pseudo-breakpoint, so thanks a lot for the link.:)

 

(defun compstring (lst / )
(setq str "") 
 (if (= (length lst) 0) (setq str "NONE."))
 (if (= (length lst) 1) (setq str "ONE."))
 (if (> (length lst) 1) (setq str "MORE THAN ONE."))
)

Link to comment
Share on other sites

After stepping through that part of the routine one by one, I still can't figure out what's going on. AutoCAD doesn't throw an error or anything; the string just won't receive the value returned by the function when the length of the list going into it is 0 or 1, despite the fact that the str variable and everything else inside the function is updated as it should.

 

Does it make a difference that I'm running these three kinds of strings in sequence? The lists I'm working with were all created from the same sheet.

Link to comment
Share on other sites

I think you had a few extra parentheses in your cond items for the length=1 and t. It works fine for me with this code:

(setq list1 '(("string1" 2)
             ("string10" 1)
             ("string11" 1)
             ("string2" 2)
             ("string3" 1)
             ("string4" 1)
             ("string5" 7)
             ("string6" 2)
             ("string7" 1)
             ("string8" 1)
             ("string9" 1)
            )
     list2 '(("string" 1))
     list3 nil
)

(defun makestring (lst /)
 (setq str "")
 (cond
   ((= (length lst) 0) (setq str "STRING X"))
   ((= (length lst) 1) ;|(do stuff)|; (setq str "STRING Y"))
   (T ;|(do other stuff)|; (setq str "STRING Z"))
 )
)

Testing code in the Lisp Console:

_$ (makestring list1)
"STRING Z"
_$ (makestring list2)
"STRING Y"
_$ (makestring list3)
"STRING X"

The 3 if statements shouldn't give different results from using the cond, it's just that it's less efficient since all 3 conditions would always be checked. And in lisp the last operation would be returned out of the function. In your case it's what happens with the last if statement. E.g. if the length is 0 or 1 the last if statement returns nil ... which is then returned out of the function.

 

If you want to make your function return whatever's inside the str variable, just add str (without parentheses) as the very last item in your defun. Something like this:

(defun compstring (lst /)
 (setq str "")
 (if (= (length lst) 0)
   (setq str "NONE.")
 )
 (if (= (length lst) 1)
   (setq str "ONE.")
 )
 (if (> (length lst) 1)
   (setq str "MORE THAN ONE.")
 )
 str
)

But, I'd still go with cond instead (in this case).

Link to comment
Share on other sites

After I made my last post I started experimenting by switching around the if statements, and indeed the only one that would return was the last one in the sequence. It never occurred to me to add the "str" at the end. *facepalm*

 

Anyway, I've since gone back to the conditional format.

Link to comment
Share on other sites

Anyway, I've since gone back to the conditional format.
Glad you got it working. The cond is usually a much better idea than multiple ifs. E.g. to get it working exactly like your cond does you must use nested ifs:
(if (= (length lst) 0)
 (setq str "STRING X")
 (if (= (length lst) 1)
   (setq str "STRING Y")
   (setq str "STRING Z")
 )
)

Actually this idea of Lisp returning the last thing calculated makes it possible to work with what's known as "functional programming". E.g. you could have rewritten your code thus:

(setq str
 (cond
   ((= (length lst) 0) "STRING X")
   ((= (length lst) 1) "STRING Y")
   (t "STRING Z")
 )
)

I.e the last thing that happened in the cond is used to setq str, but also the setq returns the value it placed into str.

 

Once you figure this out you'll find very powerful stuff in lisp, which you can't do in many other languages. Or at least you need to program huge quantities to get the same effect.

Link to comment
Share on other sites

Can anyone please tell me what's wrong with the code below? I keep getting an error saying...

 

; error: bad argument type: stringp nil

 

...and I have no idea where it occurs because stepping through each line on debug mode for some reason is not highlighting the lines as I go through them (based on my watch window I don't think it even begins to concatenate the list). I'm at wit's end with this one part of the stupid routine I'm working on. I thought this would've been the easy part. :x

 


(defun c:test ( / )

(setq lst '("1" "2" "3" "4" "5"))
(cond 
  ((= (length lst) 0) (setq str "NONE.")) ;first cond
  ((= (length lst) 1) (setq str (strcat (car lst) "."))) ; second cond
  (T (
     (setq str "")
     (foreach x lst
     (setq str (strcat str x))))) ; end third cond 
); end cond

);end test

 

I've even tried just typing this on the command line and I still get the error:

 

(setq str (foreach x lst (strcat str x)))

 

even though !lst returns ("1" "2" "3" "4" "5")

Link to comment
Share on other sites

Try this instead:

 

(defun c:TEST  ( / [color=red]lst len str[/color])
 (setq lst '("1" "2" "3" "4" "5"))
 (cond
   ((= (setq [color=blue]len [/color](length lst)) 0)
    (setq str "NONE."))
   ((= len 1)
    (setq str (strcat (car lst) ".")))
   ((apply 'strcat lst)))
 )

** Note that the variables have been localized, and that variable "len" was added to reduce the number of times you call "(length lst)".

 

Hope this helps!

Link to comment
Share on other sites

Based on a previous post, I just removed a pair of parentheses from the T condition and now everything works fine. Grrrrrr. Maybe I just can't count, but I imagined the structure of the T case to be the same as the others.

 

(cond

( (if case 1) (do stuff))

( (if case 2) (do stuff))

( T (do stuff))

)

 

But apparently it's

 

(cond

( (if case 1) (do stuff))

( (if case 2) (do stuff))

( T do stuff)

)

 

I've spent several hours the last few days sweating over this one part, and all it was was a couple of extra parentheses. I'll be facepalming myself for a while now.

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...