Adsense

2008年12月26日金曜日

[LSL] 複数の文字列を一気に置き換えたい

スクリプト カテゴリに登録しているわりに最近スクリプトの話題が少ないので、、、(回転と位置のお話の続きはそのうち、、、でも、他の方の素晴らしい記事が最近あがっているので、、、ちょっとお休みかも)

笑い話に近いのですが、、、

よくエディタとかで「置換」または「すべて置換」というメニューで、ある文字列を違う文字列に置き換えることありますよね。それを LSL でやりたい、と思いました。それも複数の文字列をそれぞれ違うものに一気に置き換えたかったのです。

検索したら、lslwiki のライブラリに Sample スクリプトがあり、また、ちょっと考えると、置き換えたいストリングの長さを取得 (llStringLength) して、置き換えたい文字列のパターンでその文字がある位置を特定 (llSubStringIndex) して、置き換えたい文字列を削除 (llDeleteSubString) (もしくはその部分をさけて)して、そこに置き換えるべき文字列をいれ、原文に戻し、また、同じ処理を繰り返し、置き換えたい文字列がなくなることを意味する llSubStringIndex の戻り値が -1 になるまで行う、、というようなスクリプトになると考えました。

そこで lslwiki のサンプルを元に書いたのが、、、こんな感じです。(普通はユーザー関数にするのでこんなことしませんが、タッチすると確認できるようにしてみました。) A を 1 に、B を 2 に、C を 3 に、、、というように E まで置き換えます。

string text = "このようなD長いA文字列をBある文字がEきたらC分解してAリストにCいれます";
list rs_checkList = ["A", "B", "C", "D", "E"];
list rs_replaceList = ["1", "2", "3", "4", "5"];

default{
    touch_start(integer total_number){
        llResetTime();
        string source = text;
        integer i = 0;
        integer max = llGetListLength(rs_checkList);
        string p = "";
        string r = "";
        integer len = 0;
        integer pos = -1;
        for (; i<max; ++i){
            p = llList2String(rs_checkList, i);
            r = llList2String(rs_replaceList, i);
            len = llStringLength(p);
            while (llSubStringIndex(source,p) > -1) {
                 pos = llSubStringIndex(source, p);
                 if (llStringLength(source) == len) {
                       source = r;
                 } else if (pos == 0) {
                       source = r+llGetSubString(source, pos+len, -1);
                 } else if (pos == llStringLength(source) - len) {
                       source = llGetSubString(source, 0, pos-1) + r;
                 } else {
                       source = llGetSubString(source, 0, pos-1) + r + llGetSubString(source, pos+len, -1);
                 }
             }
         }
         llSay(0,text);
         llSay(0,source);
         llSay(0,(string)llGetTime());
     }
}

でも、これらの処理が変数の宣言を除くと、たった2行程度で終わる、、、のを知って愕然としました。(笑

llParseString2List / llParseStringKeepNulls で文字列分解

ポイントは llParseString2List や llParseStringKeepNulls を使って、指定文字列で List に分解して、llDumpList2String のセパレータに置き換えたい文字をいれて、分解された List をつなげることを繰り返す、という方法です。
string text = "このようなD長いA文字列をBある文字がEきたらC分解してDリストにCいれます";
list rs_checkList = ["A", "B", "C", "D", "E"];
list rs_replaceList = ["1", "2", "3", "4", "5"];
default{
    touch_start(integer total_number)    {
        llResetTime();
        string source = text;
        list sourceList = [];
        integer num = llGetListLength(rs_checkList);
        integer i = 0;
        for(;i<num;++i){
            sourceList = llParseStringKeepNulls(source, llList2List(rs_checkList, i, i), []);
            source = llDumpList2String(sourceList, llList2String(rs_replaceList, i));
        }
        llSay(0,text);
        llSay(0,source);
        llSay(0,(string)llGetTime());
    }
}
み、、、短い、、、(@@
気を取り直して、llParseStiring2List や llParseStringKeepNulls と文字列、置き換え、string, replace などで検索すると、文字列の置き換えで皆さんあたりまえのようにやられていることを発見、、、。
処理時間もあきらかに短くなります。半分以下ですね。
気がついてよかった、、、と思わされた一件でした。