テキストの文字列を一括で置換する|AutoCAD VBAマクロの作成方法
本ページではVBAを使ってAutoCADのモデル空間名に存在するテキストの文字列を一括で置換する方法を解説していきます。(ここでいう”テキスト”はマルチテキスト/文字記入で生成された要素のこと)
機能としてはAutoCAD標準の[検索と置換](FIND)コマンドとほぼ同等ですが、VBAマクロで作成することにはいくつかのメリットがあります。VBAマクロを使用する場合、標準機能には存在しない細かい条件を追加できるため、例えば指定色のテキストや指定のフォントサイズのテキストのみを対象とした文字列置換を柔軟に行うことができます。
テキストの文字列を一括で置換する
本ページではテキスト内の文字列を一括で置換するマクロを解説していきます。
サンプルコードは下記のような仕様になっています。
・ブロック内にテキストがある場合はブロック内のテキストの文字列も置換する
・置換前と置換後の文字列はコード内で変更可能。
※マルチテキストで意図しない置換が行われる可能性あり(詳細はコード解説参照)
サンプルコード
下記はアクティブドキュメントのモデル空間内にあるテキストの文字列を一括で置換するサンプルコードです。ブロックが存在する場合も、ブロック内のテキストにアクセスして文字列の置換を行います。コード17,18行目の「sBef」と「sAft」の値を変更することで任意の文字に置換することが出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
'---------------------------------------------------------------------------------- '- '- メイン処理 '- '---------------------------------------------------------------------------------- Sub main() Dim oEntity As AcadEntity Dim oEntity2 As AcadEntity Dim oBlockRef As AcadBlockReference Dim oBlock As AcadBlock Dim sBlockName As String Dim cReplacedBlocks As Collection Dim sBef As String Dim sAft As String sBef = "置換前文字列" sAft = "置換後文字列" Set cReplacedBlocks = New Collection 'モデル空間内要素ル-プ For Each oEntity In ThisDrawing.ModelSpace Select Case TypeName(oEntity) '■マルチテキストか文字の場合 Case "IAcadMText", "IAcadText" '文字列の置換 oEntity.TextString = Replace(oEntity.TextString, sBef, sAft) '■ブロックのインスタンスの場合 Case "IAcadBlockReference" 'ブロック名称の取得 Set oBlockRef = oEntity sBlockName = oBlockRef.EffectiveName '名称からインスタンス元のブロックを取得 Set oBlock = ThisDrawing.Blocks.Item(sBlockName) 'コレクション内に同名のブロックが存在するか確認 If IsIncludeCollection(cReplacedBlocks, oBlock) = False Then 'ブロック内要素ル-プ For Each oEntity2 In oBlock If TypeName(oEntity2) = "IAcadMText" Or _ TypeName(oEntity2) = "IAcadText" Then 'マルチテキストか文字の場合は文字列の置換 oEntity2.TextString = Replace(oEntity2.TextString, sBef, sAft) End If Next '置換したブロックをコレクションに保存 Call cReplacedBlocks.Add(oBlock) End If End Select Next '再作図(描画更新) Call ThisDrawing.Regen(acActiveViewport) End Sub '---------------------------------------------------------------------------------- '- コレクション内に同名のブロックが存在するかを判定する '- cBlocks :確認対象コレクション '- oBlock :存在確認するブロック '- 戻り値 :[True] 存在する / [False] 存在しない '---------------------------------------------------------------------------------- Private Function IsIncludeCollection(cBlocks As Collection, oBlock As AcadBlock) As Boolean Dim oBlockLoop 'As AcadBlock For Each oBlockLoop In cBlocks If oBlockLoop.Name = oBlock.Name Then IsIncludeCollection = True Exit Function End If Next IsIncludeCollection = False End Function |
コード解説
サンプルコードの大まかなフローは下記の通りです。
(要素がブロックである場合はブロック内の要素でさらにループ)
② 要素がテキストである場合は文字列の置換を行う
アクティブドキュメントのモデル空間内の要素をループ
Dim oEntity As AcadEntity
For Each oEntity In ThisDrawing.ModelSpace
Select Case TypeName(oEntity)
Case “IAcadMText”, “IAcadText”
‘oEntityを使った処理
Case“IAcadBlockReference“
’oEntityを使った処理
End Select
Next
モデル空間内の要素がテキストかの条件分岐を行うにはTypeName関数を使ってoEntityのオブジェクトタイプを調べることで判定することができます。単一行テキストの場合は「IAcadText」、マルチテキストの場合は「IAcadMText」と別々に取得することができますが、ここではテキスト内の文字列置換を行うだけの共通処理のため、いずれかのテキストであれば置換処理を行うようにしています。
また、上記の条件だけではブロック内のテキストにアクセスすることはできません。ブロック内のテキストにアクセスするにはモデル空間内の要素でループ処理を行うのと同じように、ブロック内の要素でさらにループを行う必要があります。そのためには要素がブロックであるという判定を行う必要があります。これはテキスト判定と同じでオブジェクトタイプが「IAcadBlockReference」であるかを確認すれば判定ができます。モデル空間内に配置されているブロックはオリジナルのデータがインスタンス化(複製)されたものであるため、名前には”参照”を表すReferenceという文字が付与されています。
要素がブロックである場合はブロック内の要素でさらにループ
Set oBlock = ThisDrawing.Blocks.Item(oBlockRef.EffectiveName)
oBlockRefがインスタンス化されたブロック要素(AcadBlockReferenceオブジェクト)で、oBlockがそのインスタンス元のブロック要素(AcadBlockオブジェクト)です。よって、このoBlock内にあるテキストの文字列を置換すれば、このoBlockからインスタンス化して作成されたすべてのoBlockRefの文字列もそれに合わせて自動的に変更されることになります。
インスタンス元のデータが同じブロック要素は多数存在するので、インスタンス化されたブロックの数によっては何度もインスタンス元のブロックにアクセスしてしまいます。そこで、サンプルコードでは1度置換処理を行ったブロックは2度目以降は無視をするという処理を行っています。これはCollectionに変更したブロック要素を格納していくことで、既に置換処理を行ったブロック要素なのか行っていないブロック要素なのかを判定することができるようにしています。
ブロック内要素のループは基本的にモデル空間内のループと考え方は同じで下記のように書きます。
このブロック内の要素でもモデル空間と同じようにテキスト判定を行えばモデル空間内にあるテキストだけでなく、モデル空間内のブロック内にあるテキストの文字列の置換もまとめて行うことができるようになります。(逆をいえばこの処理を無くせばブロック内テキストは置換の対象外にもできる)
Dim oEntity2 As AcadEntity
For Each oEntity2 In oBlock
‘oEntity2を使った処理
Next
テキスト内の文字列置換を行う
oEntity.TextString = Replace(oEntity.TextString, sBef, sAft)
文字列の置換にはReplace関数を使います。引数として置換対象の文字列(oEntity.TextString)、置換前の文字列(sBef)、置換後の文字列(sAft)を入力することで、戻り値として置換された文字列を取得することができます。これをすべてのテキストに対して行えば文字列の一括置換が実現できます。
1 2 3 |
' ' "{\fMS PGothic|b0|i0|c128|p50;テキスト}" ' |
このうち画面に表示されている文字列は”テキスト”の部分だけで、それ以外の文字列は全てその”テキスト”という文字列に係る情報となっています。この各情報の値を変更することで色やフォントを変えることもできるため使いようによっては便利ですが、今回のサンプルマクロのような置換を行う際に情報部分の文字列の置換が行われてしまう可能性があるため注意が必要です。
これを対応するには、一旦テキストの部分と情報の部分の文字列を別に切り分け、テキスト部分の置換をして、再度情報の部分に組み付けるという手間のかかる処理が必要になります。
上記処理により文字列の置換は行えますが、内部的な処理が済んだだけで画面にはすぐに反映されません。VBAマクロ実行後にAutoCAD内をクリックしたりカメラ操作を行えば変更結果が表示されますが、何も操作しないと置換処理ができていないように見えてしまいます。そこで、明示的に画面を再描画する方法として下記のコードを処理の最後に実行しています。
Call ThisDrawing.Regen(acActiveViewport)
この処理を行うことで、AutoCAD内のアクティブなビューポートが再作図、すなわち画面描画の更新が行われます。AutoCADのVBAマクロでは最後の仕上げとして、この再作図処理を実行することが多いです。引数をacAllViewportsとすることですべてのビューポートの再作図を行うことも可能です。
まとめ
サンプルコードでは全てのテキストに対して文字列置換を適用させていましたが、条件分岐文を追加して指定の色や指定のフォントサイズのテキストだけを文字列置換の対象にすることもできます。
また、モデル空間だけでなくレイアウト空間(ペーパー空間)を対象にしたり、フォルダ内のすべてのドキュメント(.dwg)を対象にしたりとコードを改変することで様々な応用マクロを作成することも可能です。ぜひ作業効率の上がるような独自のマクロを開発してみて下さい。
「作りたいものはあるけど作り方がわからない!」という方は下記リンク先のメインページでAutoCADのVBAマクロ案を募集しているのでぜひ勉強にご利用ください。