davidson_cesar Posted March 5, 2011 Share Posted March 5, 2011 Hi guys! I need your help. I need a lisp to find and change words at once in a Autocad drawing. For example: I need to change the words "milk to coffe, water to wine and juice to beer". Is it possible? I'm waiting for an answer. Thanks! Quote Link to comment Share on other sites More sharing options...
Lee Mac Posted March 5, 2011 Share Posted March 5, 2011 Very possible, try the FIND command; no need for a LISP. Quote Link to comment Share on other sites More sharing options...
alanjt Posted March 5, 2011 Share Posted March 5, 2011 Very possible, try the FIND command; no need for a LISP. Actually, if he's using 2000, FIND was nothing more than a simple LISP Express Tool. Quote Link to comment Share on other sites More sharing options...
Tharwat Posted March 5, 2011 Share Posted March 5, 2011 Here it goes ..... (defun c:TesT (/ str NewStr ss) ; Tharwat 05. 03. 2011 (if (and (not (eq "" (setq str (getstring "\n Enter text to Find :")))) (not (eq "" (setq NewStr (getstring "\n Enter Text to be replaced :")))) (setq ss (ssget "_X" (list '(0 . "MTEXT,TEXT") (cons 1 str) ) ) ) ) ((lambda (i / ss1 e) (while (setq ss1 (ssname ss (setq i (1+ i)))) (setq e (entget ss1)) (entupd (cdr (assoc -1 (entmod (subst (cons 1 NewStr) (assoc 1 e) e))) ) ) ) ) -1 ) (princ "\n No matching texts Found ") ) (princ) ) Tharwat Quote Link to comment Share on other sites More sharing options...
irneb Posted March 5, 2011 Share Posted March 5, 2011 Tharwat, again I'd advise against using entmod on text. It causes problems with annotative objects. But the OP is on 2000 so it shouldn't give much hassles. BTW, your code would only modify text which is completely the same as the text to find. What about if the word to change is part of a sentence? You could go and change the (cons 1 str) to (cons 1 (strcat "*" str "*")), but that would simply now select all the texts containing the relevant word. To replace a word inside a text string look into the vl-string-subst function. Otherwise (if you don't want the vl stuff) you'll need to step through the text and check for the word that way. But heck! As Lee's stated, the FIND thingy already does so (even if it's an Express tool in 2000) ... and it can handle capitalization unlike the above methods. Quote Link to comment Share on other sites More sharing options...
davidson_cesar Posted March 5, 2011 Author Share Posted March 5, 2011 Thanks guys for replying. But I’m thinking in something different. I’m looking for something like a multfind (serch and replace) command. For example, I would make a data tab at first with the words to find and the words to translate. And after, with just ONE command, I would translate all the words. Example: Find word - Translate to milk ---------- cofee water --------- wine juice -----------beer Quote Link to comment Share on other sites More sharing options...
Lee Mac Posted March 5, 2011 Share Posted March 5, 2011 Also, what about MText with formatting codes, and all the other objects in which text may be found: Leaders/Dimensions/Block Attributes... This seems like re-inventing the wheel big-time! Quote Link to comment Share on other sites More sharing options...
irneb Posted March 5, 2011 Share Posted March 5, 2011 OK then try this: (vl-load-com) (setq *QuickFR:Words* '(("milk" . "coffee") ("water" . "wine") ("juice" . "beer")) ) (defun str-search (pat str / p) (if (setq p (vl-string-search (strcase pat) (strcase str))) (substr str (1+ p) (strlen pat)) ) ) (defun QuickFR (ss / obj item str found changed) (vlax-for obj (vla-get-ActiveSelectionSet (vla-get-activedocument (vlax-get-acad-object))) (setq str (vla-get-TextString obj) found nil) (foreach item *QuickFR:Words* (while (setq found (str-search (car item) str)) (setq str (vl-string-subst (cdr item) found str) changed t) ) ) (if changed(vla-put-TextString obj str)) ) ) (defun c:QuickFR (/ ss) (if (setq ss (ssget "_:L" '((0 . "TEXT,MTEXT")))) (QuickFR ss) ) (princ) ) (defun c:QuickFR_All (/ ss) (if (setq ss (ssget "_X" '((0 . "TEXT,MTEXT")))) (QuickFR ss) ) (princ) ) The QuickFR allows you to select the texts you want to search through, the QuickFR_All just searches throughout the entire drawing. Change the list at the top to match the old & new words (add more if you like). No need to worry about case sensitivity, since milk would match all of Milk, mILk, miLK, etc. If you want case sensitivity then change the portion looking like this: (while (setq found (str-search (car item) str)) (setq str (vl-string-subst (cdr item) found str) changed t) ) into this: (while (vl-string-search (car item) str) (setq str (vl-string-subst (cdr item) (car item) str) changed t) ) And you can then omit the str-search defun. Quote Link to comment Share on other sites More sharing options...
irneb Posted March 5, 2011 Share Posted March 5, 2011 A few caveats to this (apart from Lee's very valid points). The search would find any word/portion thereof. So you can quite easily change milkrun into coffeerun - the code doesn't cater for whole words at all. Also any fields that may be linked into the text would be lost (unlike what happens with the normal FIND command). Quote Link to comment Share on other sites More sharing options...
Lee Mac Posted March 5, 2011 Share Posted March 5, 2011 Irneb, I would be inclined to use something like this to substitute the string: (defun _StringSubst ( new old string / newlen i ) (setq newlen (strlen new) i 0) (while (and (< i (strlen string)) (setq i (vl-string-search old string i))) (setq string (vl-string-subst new old string i) i (+ i newlen)) ) string ) to account for cases such as: ("is" . "this") Quote Link to comment Share on other sites More sharing options...
davidson_cesar Posted March 5, 2011 Author Share Posted March 5, 2011 Thanks one more time for your attention! Cheers! Quote Link to comment Share on other sites More sharing options...
irneb Posted March 5, 2011 Share Posted March 5, 2011 Thanks Lee! This was a quickie, so I didn't think it through fully. Thankfully there's some truly hawk-eyed guys around here Quote Link to comment Share on other sites More sharing options...
Tharwat Posted March 5, 2011 Share Posted March 5, 2011 Tharwat, again I'd advise against using entmod on text. It causes problems with annotative objects. But the OP is on 2000 so it shouldn't give much hassles. BTW, your code would only modify text which is completely the same as the text to find. What about if the word to change is part of a sentence? You could go and change the (cons 1 str) to (cons 1 (strcat "*" str "*")), but that would simply now select all the texts containing the relevant word. To replace a word inside a text string look into the vl-string-subst function. Otherwise (if you don't want the vl stuff) you'll need to step through the text and check for the word that way. But heck! As Lee's stated, the FIND thingy already does so (even if it's an Express tool in 2000) ... and it can handle capitalization unlike the above methods. Thank you irneb for your advise . In regard to Annotative texts , I have tried my codes after you have commented it and I found it working with it and replacing the text of it. And I admit that codes would replace only a complete texts that included within the searched texts. Many thanks Quote Link to comment Share on other sites More sharing options...
irneb Posted March 5, 2011 Share Posted March 5, 2011 n regard to Annotative texts , I have tried my codes after you have commented it and I found it working with it and replacing the text of it. Try this: Make sure your current text style is annotative. Change your CAnnoScale to something else than 1:1. Create a text containing only one of the words to be replaced. Now run your code. What will happen is the entget obtains much more than just the text's value. It also gets hold of the currently displayed text height (which is incorrect when it's an Annotative Text). E.g. if your text style was set to be 2mm high and your CAnnoScale was 1:100, then (assoc 40 e) --->> (40 . 200.0). Now you use entmod even with the original. So you effectively set the text's height to 200, but since it is Annotative this gets multiplied by its scale factor and your text becomes 20000 high. Edit: IMO this is a fault from ADesk's side - they didn't think annotative scales through sufficiently. This isn't the only problem with AS, but it's one which caused me to have to rework a lot of my old routines. BTW, you can still use the entmod - you just have to make sure the code 40 is converted back to the Paper Space text height for that text. Or more easily simply omit it from the list you send to entmod (it only needs the -1 . ename and any codes which you want modified - the others are wasted space anyway). But I like using the vla methods for this purpose, it's a slight bit easier (to me at least). Though the VLA methods have other hiccups: e.g. if your text contains some strange characters (notably unicode) this will change to question marks ???? So there's a catch 22 situation here. Quote Link to comment Share on other sites More sharing options...
Tharwat Posted March 5, 2011 Share Posted March 5, 2011 Try this: Make sure your current text style is annotative. Change your CAnnoScale to something else than 1:1. Create a text containing only one of the words to be replaced. Now run your code. What will happen is the entget obtains much more than just the text's value. It also gets hold of the currently displayed text height (which is incorrect when it's an Annotative Text). E.g. if your text style was set to be 2mm high and your CAnnoScale was 1:100, then (assoc 40 e) --->> (40 . 200.0). Now you use entmod even with the original. So you effectively set the text's height to 200, but since it is Annotative this gets multiplied by its scale factor and your text becomes 20000 high. Great information about Annotative texts. I face it as you have described it entirely. Thank you so much. But a question came up to my mind right now with these Anno. texts. For example . if we use (entget (car (entsel))) to get the dxf data of the Anno. text , we would not get the correct info about the height of it. So how to get the real height of Anno.texts to be replaced later on with another text style or with a text using it's original height ? many thanks Quote Link to comment Share on other sites More sharing options...
irneb Posted March 5, 2011 Share Posted March 5, 2011 So how to get the real height of Anno.texts to be replaced later on with another text style or with a text using it's original height ?You need to obtain the AnnoScale attached to the entity. And since there could be more than one scale attached (wich may or may not equal the CAnnoScale) this becomes a bit complex. E.g. The annotative text will contain a portion like this: (102 . "{ACAD_XDICTIONARY") (360 . ) (102 . "}") That 360 code points to the XDictionary attached to the text, which in turn can look like this:((-1 . ) (0 . "DICTIONARY") (330 . ) (5 . "14E") (100 . "AcDbDictionary") (280 . 1) (281 . 1) (3 . "AcDbContextDataManager") (360 . )) Now the thing is that AcDbContaxtDataManager. You can get hold of it through the 360 code directly following it, but the more official way would be to use dictsearch on the ename of the main dictionary: (setq cm (dictsearch (cdr (assoc 360 e)) "AcDbContextDataManager")) This should give something like: ((-1 . ) (0 . "DICTIONARY") (5 . "14F") (102 . "{ACAD_REACTORS") (330 . ) (102 . "}") (330 . ) (100 . "AcDbDictionary") (280 . 0) (281 . 1) (3 . "ACDB_ANNOTATIONSCALES") (350 . )) So you see a ACDB_ANNOTATIONSCALES entry into the content manager dictionary, followed by a 350 code with an ename. Again the dictsearch would work:(setq asd (dictsearch (cdr (assoc -1 cm)) "ACDB_ANNOTATIONSCALES")) Now this gives you the following (I've got 2 scales attached to this text (1:100 and 1:50): (setq asd (dictsearch (cdr (assoc -1 cm)) "ACDB_ANNOTATIONSCALES")) ((-1 . <Entity name: 7efa4280>) (0 . "DICTIONARY") (5 . "150") (102 . "{ACAD_REACTORS") (330 . <Entity name: 7efa4278>) (102 . "}") (330 . <Entity name: 7efa4278>) (100 . "AcDbDictionary") (280 . 0) (281 . 1) (3 . "*A1") (350 . <Entity name: 7efa4288>) (3 . "*A2") (350 . <Entity name: 7efa4340>)) Can you see them anywhere there? No? That's because they're hidden behind those *A1 and *A2 items. So to get at them you step through this dictionary using dictnext: (setq sc (dictnext (cdr (assoc -1 asd)) t)) (while sc (setq scLst (cons sc scLst) sc (dictnext (cdr (assoc -1 asd)))) ) Which gives you this: (((-1 . <Entity name: 7efa4340>) (0 . "ACDB_TEXTOBJECTCONTEXTDATA_CLASS") (5 . "168") (102 . "{ACAD_REACTORS") (330 . <Entity name: 7efa4280>) (102 . "}") (330 . <Entity name: 7efa4280>) (100 . "AcDbObjectContextData") (70 . 3) (290 . 0) (100 . "AcDbAnnotScaleObjectContextData") (340 . <Entity name: 7efa40b8>) (70 . 0) (50 . 1.5708) (10 1256.63 992.444 0.0) (11 0.0 0.0 0.0)) ((-1 . <Entity name: 7efa4288>) (0 . "ACDB_TEXTOBJECTCONTEXTDATA_CLASS") (5 . "151") (102 . "{ACAD_REACTORS") (330 . <Entity name: 7efa4280>) (102 . "}") (330 . <Entity name: 7efa4280>) (100 . "AcDbObjectContextData") (70 . 3) (290 . 1) (100 . "AcDbAnnotScaleObjectContextData") (340 . <Entity name: 7efa40c0>) (70 . 0) (50 . 1.5708) (10 1256.63 992.444 0.0) (11 0.0 0.0 0.0))) Each of which points to a 340 code: (foreach sc scLst (prin1 (entget (cdr (assoc 340 sc)))) (princ "\n\n") ) Which in turn gives you the scales themselves: ((-1 . <Entity name: 7efa40b8>) (0 . "SCALE") (5 . "117") (102 . "{ACAD_REACTORS") (330 . <Entity name: 7efa4060>) (102 . "}") (330 . <Entity name: 7efa4060>) (100 . "AcDbScale") (70 . 0) (300 . "1:50") (140 . 1.0) (141 . 50.0) (290 . 0)) ((-1 . <Entity name: 7efa40c0>) (0 . "SCALE") (5 . "118") (102 . "{ACAD_REACTORS") (330 . <Entity name: 7efa4060>) (102 . "}") (330 . <Entity name: 7efa4060>) (100 . "AcDbScale") (70 . 0) (300 . "1:100") (140 . 1.0) (141 . 100.0) (290 . 0)) So now you have a list of scale entities attached to that one single text. Each of these has its name in the 300 code, its paper space units in 140 and its model space units in 141. But, say your current CAnnoScale="1:200". Which one of these is actually shown? The answer would be the 1st in the dxf codes - i.e. the last one obtained in the list you saved (since you used cons to generate that list). In this case (300 . "1:100") (140 . 1.0) (141 . 100.0). Now from that you can calculate the scale factor: (#140 / #141) = 0.01. That you then apply to the height you obtained from the original entget (that (40 . 200.0) and you get the value 2.0. See why it's soooo much simpler just using the vla methods? Or even just ignoring the height for entmod? Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.