第二回『設定ファイルで誰にでも使いやすく』
ソースを読めない知人にスクリプトを配布する時、設定ファイルの書き換えだけで、変
数をカスタマイズ
まだフリー・ウェアで満足してい
ますか? ”楽ちん”にPerlスクリ
プトを作りましょう。今回は、ソー
スを読めない知人にスクリプトを配
布する時、設定ファイルの書き換え
だけで、変数をカスタマイズできる
テクニックの紹介です。
●めぐみちゃんとアニメ(ゲーム)脚本
25歳の時、好きな娘がいました。
名前はF川めぐみちゃん、可愛い目
をしているのに、ひねくれていて生
意気で。ある日届いたハガキには、
宮崎県K市に嫁いだと書かれていま
した。お互い、連絡も取らなくなり、
今では、彼女は、子供もいないまま、
離婚してしまい、実家の造り酒屋に
戻ったと聞きます。めぐみちゃん、
アニメ脚本は、やめちゃったのかな
……?
……私的なお話を、大変、失礼し
ました(*^.^*)。昔、TV脚本家の
先生について勉強していためぐみち
ゃんに頼まれて、アニメ脚本入力支
援ソフトを作ったことがあります。
今回は、その時のことを思い出して、
Perlで組んで見ました。
脚本のフォーマットをご存じでし
ょうか? 非常に特殊なインデント
で、細かな直しが頻発する業界のこ
と、直しの度に、インデントをいち
いち、手打ちでやっていたら、とて
も大変です。
脚本形式のサンプルです。
−−−−−−−−−−−−−
<サンプル1>
「銀河伝説オイレーン」
第三稿 2001.5.21
F川めぐみ
○ 宇宙
円盤の大群が、地球にゆっくり
近づいてくる。
○ 商店街(平和な日曜日で、通行
人が大勢いる)
さくらとイシューがソフトクリ
ームを食べて歩いている。
さくら「ふがふが……」
ちゅどーん! と、絨毯爆撃が
始まる。一瞬にして商店街は戦
場となる。
イシュー「(腕の無線)大森くん!
大森くん! 第一次戦闘配備よ!」
−−−−−−−−−−−−−
脚本を、手動で、ちょっと更新し
ようとすると、インデントのやり直
しが実に煩わしそうです。キーボー
ドの苦手なめぐみちゃんは、これが
大変だったのです。
−−−−−−−−−−−−−
<サンプル2>
N=改行を意味します。
「銀河伝説オイレーン」N
第三稿 2001.5.21N
F川めぐみN
○宇宙N
円盤の大群が、地球にゆっくり近づ
いてくる。N
N
○商店街(平和な日曜日で、通行人
が大勢いる)N
さくらとイシューがソフトクリーム
を食べて歩いている。N
さくら「ふがふが……」N
ちゅどーん!と、絨毯爆撃が始まる。
一瞬にして商店街は戦場となる。N
イシュー「(腕の無線)大森くん!
大森くん!第一次戦闘配備よ!」N
−−−−−−−−−−−−−−
こんな、ベタ打ちテキストなら、
更新は楽です。これが、一発で、自
動的に変換されたら……。でも、簡
単そうですが、よーく見て下さい。
このインデントは、ちょっとやそっ
とでは、自動化できません。
脚本には、シーン起こし、ト書き、
台詞、改行のみの、四つのブロック
があり、インデント必要なしの、特
殊行もあります。
例えば、台詞などは、開始行はイ
ンデントされず、二行目から全角1
マス、空白が開けられます。ト書き
は、2マス。納品先によっては、3
マスの場合などもあるそうです。同
時に、ぶら下げ禁則位はサポートし
ていないと使い物になりません。ブ
ロックの判定は、どうやりましょう?
ゲーム台本も、こうした形式を取
る場面もあるそうですから、アニメ・
ゲーム業界に興味がある人は、今回
のスクリプトは、必見です。そうで
ない人も、プログラム的に、どうや
って実現するか、興味を持ちません
か? しかも、Perlを使えば、あっ
けない程、簡単に書けるのです。
●スクリプトを他人に配る
「自分だけが使える動けばいいス
クリプト」。この連載は、それを目
指して始められました。でも、小さ
なスクリプトといえども、せっかく、
プログラムを自作できるのです。例
えば、「女の子に配って尊敬された
い」って思いませんか? もちろん、
必要としていない女の子にあげても、
「やっぱり辻クンて……」と、オタ
ク扱いされてしまうだけですが(^ ^;)。
今回のように、大多数の悩みでは
ない、わりと特殊な場面にこそ、自
作スクリプトの価値があります。
今回は、スクリプトを触れないユ
ーザーも、設定ファイルを更新する
だけで、変数のカスタマイズができ
るようにしました。
●プログラム
色々、悩んだ末、リスト1のよう
になりました。こんな短いスクリプ
トで、禁則まで処理しているのは、
テキスト処理に優れているPerlの実
力発揮と言えるでしょう。
使用方法は、設定ファイルで指定
したベタ打ち脚本と同じディレクト
リにスクリプトと設定ファイルをコ
ピーした上で、スクリプトを実行す
るだけです。
少し悩んだブロック判定は、スク
リプトに注釈したような条件で判定
するようにしました。元ファイルを
ベタ打ちする際、なるべく違和感な
く打てるようにする為、ト書きの判
定に幅をもたせ、ユーザーに自由度
を与えています。
&mvichimoji2は、$_の左辺一文字
を$tsugimojiに入れ、$_の左辺文字
をカットします。
禁則は、"?!"、"。」"等は、
二文字までぶら下げ、"」"や")"等は、
一文字のみぶら下げられます。尚、
このスクリプトでは、行末の"("を
次行に送る、送り出し禁則は行われ
ません。
length ($_)は、改行も一文字に
カウントすることに注意して下さい。
また、untilループで、元ファイルの
一行を一文字一文字処理して、複数
行に分割しているアルゴリズムも注
目してみて下さい。
台詞ブロックの一行目、二行目で
インデントが異なる部分の処理は、
フラグ変数$kakidashiにより、判定
しています。
尚、http://www.geocities.co.jp
/SiliconValley-Cupertino/6103/に、
この連載の補足情報のHPを作りま
したので、参照して下さい。スクリ
プトなども、こちらから直接ゲット
できます。
●<今月のワンポイント>
●設定ファイルの読み込み
今月のワンポイントです。
−−−−−−−−−−−−−
#daihon-cfg.txt
#daihon.plの定義ファイル
↓入力ファイル
$daihon = "daihon.txt";
↓出力ファイル
$outtxt = "daihon2.txt";
↓一行の文字数(全角換算)
$oneline = 24;(普通40か60)
↓シーン起こし一行目冒頭文字
$okoshi1 = "○□";
↓シーン起こし二行目冒頭文字
$okoshi2 = "□";
↓台詞ブロック一行目冒頭文字
$serifu1 = "";
↓台詞ブロック二行目冒頭文字
$serifu2 = "□";
↓ト書き一行目冒頭の文字
$togaki1 = "□□";
↓ト書き二行目冒頭の文字
$togaki2 = "□□";
#注。派閥などにより(?)、
#togaki1と2は、"□□□"、
#okoshi2は、"□□"の場合もある
#ようです。
−−−−−−−−−−−−−
※↑上記、紙面で表現するため、全
角空白を□で表してあります。入力
する際は、注意。
上記を"daihon-cfg.txt"のファイ
ル名で、スクリプトと一緒に保存し
て下さい。これを更新することによ
り、一行を何文字で改行するか、シ
ーン起こし、ト書き、台詞の各ブロ
ックのインデント文字を簡単に変更
できるようにしました。これなら、
スクリプトを読めないユーザーにも、
設定ファイルだけをカスタマイズす
れば、その時々に合ったインデント
を自由に設定することができます。
設定ファイルの読み込みは、サブ
ルーチン&cfgreadにより行っていま
す。ここでは、
|$daihon = "daihon.txt";
|$outtxt = "daihon2.txt";
|$oneline = 40;
|$okoshi1 = "○ ";
などのように、最初に、デフォル
ト値を与えています。これは、万一、
カレントディレクトリにdaihon-cfg
.txtが見当たらない場合、デフォル
ト値で強制的に処理を行うためです。
設定ファイルが見当たらない場合、
処理を中止しても良いのですが、そ
れより、デフォルト動作を強行した
方が、ユーザーが誤った使用をした
場合、原因の特定にもつながり、良
いことが多いと思います。
次の、
|if (/^\$oneline *=/){
| $oneline = $_;
|$oneline =~ s/^.*= *(.*)\;.*$/$1/;
|}
は、設定ファイルの行に、"$onel
ine ="と言う文字が含まれていれば、
「= "」〜「";」までの間の文字を変
数$onelineに入れる処理をしていま
す。
このような手法で、知人に配布す
るスクリプトは、設定ファイルを別
ファイルに分けると、スクリプト使
用の敷居が低くなるでしょう。
今回のスクリプトと定義ファイル
を参考にすれば、脚本以外にも、例
えば、学会論文など、特殊インデン
トが必要なテキストの入力支援スク
リプトも作れるでしょう。応用して
見てください。
追伸。めぐみちゃんは、部外者の
私が、二次会を断って帰ろうとした
ら、腕を引いて次の店に引きずり込
んでくれた女の子なんです。このス
クリプトをめぐみちゃんの思い出に
捧げます。
#daihon.pl
#ベタ打ちテキストを脚本の形式に置換(自動インデント)する
#jperl動作用スクリプト
#s-jis保存
#cgiではなく、ダブル・クリックにより実行、または、MS-DOS窓より、実行。
&cfgread;#変数初期値
open (out, "> $outtxt");open (infile, "< $daihon");#ループ
while( <infile> ) { $sumi = 0;$nigyome = "";
$_ =~ s/ / /g;$_ =~ s/ / /g;
#↑半角空白二文字は、全角1文字に直す。半角空白が奇数の場合、最後の
#半角空白1ケは、全角空白に置換。
$_ =~ s/([!?!?])([^ !?!?」』。、))>>])/$1 $2/g;
#↑"?"+" "処理。通常、"?"の後には、" "が入る。但し、
#"?」"等のように、"」"等とセットの場合は、例外となる。
#行判定(台詞・ト書き・シーン起こし)
( ( $_ =~ /^([^ ]).*」$/ ) && ($sumi == 0) ) && &serifu;
( ( $_ =~ /^○.*$/ ) && ($sumi == 0) ) && &okoshi;
( ( $_ =~ /^([^ ]).*([^」])$/ ) && ($sumi == 0) ) && &togaki;
( ( $_ =~ /^ [^ ].*$/ ) && ($sumi == 0) ) && &togaki;
( ( $_ =~ /^ [^ ].*$/ ) && ($sumi == 0) ) && &togaki;
#" "以外で始まり、"」"で終わる→台詞
#"○"で始まる→シーン起こし
#" "以外で始まり、"」"以外で終わる→ト書き
#" "か" "で始まる→ト書き(" "で始まる場合は含めない)
#" "で始まる行は、どれにも属さず、二行目以降は、そのまま改行される。
$kakidashi = 1; $newgyo = ""; &print ; }
close (out); close (infile);
#サブルーチン
sub cfgread {
$daihon = "daihon.txt"; $outtxt = "daihon2.txt";$oneline = 40;
$okoshi1 = "○ "; $serifu1 = ""; $togaki1 = " ";
$okoshi2 = " "; $serifu2 = " "; $togaki2 = " ";
# ↑daihon-cfg.txtが存在しない場合のデフォルト値
open (infile, "< daihon-cfg.txt");
while( <infile> ) {
if (/^\$oneline *=/){ $oneline = $_; $oneline =~ s/^.*= *(.*)\;.*$/$1/; }
if (/^\$daihon *=/){ $daihon = $_; $daihon =~ s/^.*= *"(.*)"\;.*$/$1/; }
if (/^\$outtxt *=/){ $outtxt = $_; $outtxt =~ s/^.*= *"(.*)"\;.*$/$1/; }
if (/^\$okoshi1 *=/){ $okoshi1 = $_; $okoshi1 =~ s/^.*= *"(.*)"\;.*$/$1/;}
if (/^\$serifu1 *=/){ $serifu1 = $_; $serifu1 =~ s/^.*= *"(.*)"\;.*$/$1/;}
if (/^\$togaki1 *=/){ $togaki1 = $_; $togaki1 =~ s/^.*= *"(.*)"\;.*$/$1/;}
if (/^\$okoshi2 *=/){ $okoshi2 = $_; $okoshi2 =~ s/^.*= *"(.*)"\;.*$/$1/;}
if (/^\$serifu2 *=/){ $serifu2 = $_; $serifu2 =~ s/^.*= *"(.*)"\;.*$/$1/;}
if (/^\$togaki2 *=/){ $togaki2 = $_; $togaki2 =~ s/^.*= *"(.*)"\;.*$/$1/;}
}
close (infile); chomp $okoshi1; chomp $serifu1; chomp $togaki1;
chomp $okoshi2; chomp $serifu2; chomp $togaki2;
}
sub okoshi { $nigyome = $okoshi2; $_ =~ s/^○ *(.*)$/$okoshi1$1/;$sumi = 1; }
#↑○以降に、 が続く場合は、無視される。
sub serifu { $nigyome = $serifu2; $_ =~ s/^(.*)$/$serifu1$1/; $sumi = 1; }
sub togaki { $nigyome = $togaki2; $_ =~ s/^ *(.*)$/$togaki1$1/;$sumi = 1; }
sub print {
until ( length ($_) < $oneline ) { &ichigyo;print out $newgyo , "\n"; }
#↑$_が、規定文字数($oneline)未満になるまで、&ichigyoを繰返しファイル出力
$_ =~ s/[ ][ ]*$//;
( ( length ($_) >= 1 ) && ( $kakidashi == 1 )) && print out $_;
#↑ブロックが一行だけだった場合の処理。length ($_)=1は、改行のみの行。
#(length ($_) > 1)に条件を変えると、改行のみの行が削除される。
( ( length ($_) > 1 ) && ( $kakidashi == 0 )) && print out $nigyome , $_;
#↑ブロックが二行以上だった場合、上のuntilループで余った$_が処理される。
#length ($_)=1の場合、残りの処理文字が「改行」だけである。既に直前の処理
#で改行されているので、この場合、この改行コードは、棄てられる。
#length ($_) >= 1 にすると、タイミングにより、無駄な改行が入る場合がある。
}
#↓$_から、$newgyoに、文字列を移動して行き、一行が規定文字数($oneline)に
#なった場合、禁則処理後に、一行ファイル出力される。一行目の処理と二行目
#以降の処理は、フラグ($kakidashi)により、判定され、別処理が施される。
sub ichigyo {
if ( $kakidashi == 1 ) { $newgyo = ""; $kakidashi = 0; }
else { $newgyo = $nigyome; }
until ( ( length ($_) <= 1 ) || ( length ($newgyo) >= $oneline ) ) {
&mvichimoji2; }
&kinsoku; $newgyo =~ s/[ ][ ]*$//; }
#↓二種類のぶら下げ禁則を設けた。"?!"、"。」"等は、二文字までぶら下げ
#される。"」"や")"等は、一文字のみ禁則される。
#尚、"("等の送り出し(次の行頭に送ってしまう)は処理していない。
sub kinsoku { &tsugi;
if ( $tsugimoji =~ /、|。|?|!|\?|\!/ ) {
&mvichimoji1; &tsugi;
$tsugimoji =~ / |?|!|\?|\!|」|』|>|\>|)|\)/ && &mvichimoji1; return; } if ( $tsugimoji =~ / |」|』|>|\>|)|\)|・/ ) { &mvichimoji1; } }
sub mvichimoji2 { &tsugi; &mvichimoji1; }
sub mvichimoji1 { $newgyo = $newgyo . $tsugimoji; $_ =~ s/^.(.*)$/$1/; }
sub tsugi { $tsugimoji = $_; $tsugimoji =~ s/^(.).*$/$1/; chomp $tsugimoji; }
#↑chompで、$tsugimojiに付与されてしまう、末尾の改行をカットします
