株式会社デジタル・フロンティア-Digital Frontier

Header

Main

  • TOP
  • DF TALK
  • ねえ貴方たち正規表現使ってみなさいよ、捗りましてよ

ねえ貴方たち正規表現使ってみなさいよ、捗りましてよ

2011/4/12

Tag: ,,,,

はじめまして、ikaです。開発所属ですが未だ開発作業の経験が無いちょっぴり立場が微妙なTDです。

○何故正規表現を使うか

CGデザイナーは「クリエイティブな仕事」なんてたまに言われたりしますが、実際のところCG制作の作業のほとんどがクリエイティブとは程遠いルーチンワークだったりします。
そのルーチンワークは自動化出来ることが多いのでスクリプトで自動処理させると、作業時間短縮、ストレス軽減と良いこと尽くめでクリエイティブな部分に時間をかけられるようになります。
DFには開発室やセットアップチーム等制作作業を補佐する部署がありますが、規模の小さいプロダクションでは作業者自身でスクリプトを書かなくてはならないかと思います。
しかしツールを作るのはとても難しいですが、MayaやSoftimageでは操作のログがスクリプトエディタに書き出されるので、それをコピペしてスクリプトにするのは簡単です。
さらに正規表現を使えば、ログをコピペした自動化スクリプトに少し手を加えた程度でも割と汎用的なスクリプトになったりします。
ログをコピペしただけのスクリプトですと、オブジェクトの名前などが全く同じでないと動作しませんが、正規表現を使うとルールに沿った名前にしておけば動作します。
正規表現をマスターするのは難しいですが、よく使うところだけ覚えれば大丈夫です。ネットで検索すれば情報がたくさんありますし、小難しい数学の知識を覚えるよりは簡単です。

○正規表現で何が出来る?正規表現を使った例

※正規表現では(括弧)でグループ分けしてその部分を抜き出すことが出来ます。ここの例ではグループを「$数字」にしています。

・バックアップファイルの名前から日付やバージョン、コメントなどを取得
例えばシーンファイルを
S001C001_20110101_[メモ].scn
のようにしてバックアップをとっておけば
“S(\d\d\d)C(\d\d\d)_(\d\d\d\d)(\d\d)(\d\d)_\[(.*)]\.scn”
で、$1でシーンNo.、$2でカットNo.、$3~$5で保存した年月日、$6でコメントを取得できます。

キャラクタのモデルデータのバックアップを
CharaModel_v01_[メモ].mb
にしておけば
(.*)_v(\d\d)_\[(.*)].mb”
で、$1でキャラクタ名、$2でバージョン、$3でコメントを取得できます。

・ノードを複製をした際のリネーム処理
左腕(LeftArm、LeftForeArm、LeftHand)を鏡面コピーすると大抵のソフトだと複製したオブジェクトの名前を「元の名前+数字」にすると思います(LeftArm1、LeftForeArm1、LeftHand1)。
この場合は
(Left)(.*?)(\d+)
で、$1$3は必要無いので$2だけ取得して名前を「”Right”+$2」に修正できます(RightArm、RightForeArm、RightHand)。

例)
JScript

var sLeft = "LeftArm1";
var sRight = sLeft.replace(/(Left)(.*?)(\d+)/g, "Right$2");
WScript.Echo(sRight);

JScriptはreplaceメソッドに正規表現が使えるので便利です。

Python

import re
sLeft = "LeftArm1"
p = re.compile("(Left)(.*?)(\d+)")
m = p.match(sLeft)
sRight = "Right" + m.group(2)
print sRight

・ノードの名称からコンストレイン
ネームスペース違いの同じ名前にコンストレインするとか(ネームスペース部分を差し替えるのに正規表現を使う)

・MQO、OBJ、dotXSIなどのテキストベースのオブジェクトファイルの読み込み
OBJファイルの頂点座標の部分は
“v (\d+\.\d+) (\d+\.\d+) (\d+\.\d+)”
で頂点座標を取得できます。

・CG以外では
正規表現で検索、置換が出来るテキストエディタや、ファイルリネームソフトなどを使うときに役に立ったりします。

○正規表現のTips(Python)

最後に正規表現で自分が嵌った注意点とPythonの正規表現の便利機能をあげて終わりにします。

・末尾の数字を取るときの注意 (?を使う)

import re
s = "chara001"

p = re.compile("(.*)(\d+)")
m = p.match(s)
print m.group(1), m.group(2)
# chara00 1 になってしまう

p = re.compile("(.*?)(\d+)")
m = p.match(s)
print m.group(1), m.group(2)
# chara 001 になる

「.*」には数字も含まれてしまうので最後の一文字までとられてしまいます。
?を使うと次のマッチングが成立するまでになるので(この場合は\d)、数字が出てきた時点で「.*」が終わり「\d+」の方に3桁数字が入ります。

・Pythonだとmatchの結果を辞書に出来る (?P<key>を使う)

import re
s = "chara001"

p = re.compile("(.*?)(\d+)")
m = p.match(s)
print m.group(1), m.group(2)
# chara  001 になる

p = re.compile("(?P<name>.*?)(?P<num>\d+)")
m = p.match(s)
print m.group('name'), m.group('num')
# chara  001 になる

#
# 名前ルールが後から変更になった時に効果を発揮する
s = "chara_Red_001"

p = re.compile("(?P<name>.*)_(?P<variation>.*)_(?P<num>\d+)")
m = p.match(s)
print m.group(1), m.group(2)
# chara Red になってしまう
# m.group(2)が番号だったけど、順番が変わってバリエーションになってしまった
# m.group(2)を使っているところをm.group(3)に修正しなければならない

print m.group('name'), m.group('num')
# name 001 になる
# 'num'というkeyは変わらないので修正する必要が無い
Pocket

コメント

コメントはありません

コメントフォーム

コメントは承認制ですので、即時に反映されません。ご了承ください。

*