行を跨いでの前方/後方一致指定での検索のNo.10224
fzok4234 さん 20/07/19 07:20
 
お世話になっております。

さて、v8.94β4 x64 floatにて、行を跨いだ前方/後方一致指定での検索がうまくい
かず困っています。対象のテキストは、

xx
s
g
e
g
y
g
s
yy
a
g
r
g
y
g
s
zz

で、この中の「xx」と「yy」で囲まれた「g」にヒットさせるため、正規表現パターン

(?#lookbehind)(?#maxlines:50)(?#fulllinematch)(?<=(xx\s*)([a-z]\s*)*)(g)(?=
(\s*[a-z])*(\s*yy))

で検索を行ってもヒットしません。もしパターンに間違いがあればご指導の程よろし
くお願いします。




[ ]
RE:10224 行を跨いでの前方/後方一致指定No.10225
秀丸担当 さん 20/07/20 09:54
 

確かに言われているようなパターンではうまくいきませんでした。
とりあえず、[a-z]は改行は含まないので、改行を含ませるとしたら以下のような感
じになると思います。

(?#lookbehind)(?#maxlines:50)(?#fulllinematch)(?<=(xx\s*)([a-z]\s*|\n)*)(g)
(?=(\s*[a-z]|\n)*(\s*yy))

ただこれだとしても、ファイル先頭から下検索を開始したときに、最初のgにヒット
するだけになってしまいます。
gの位置から下検索を開始したときは、検索の対象となる文字列は前には遡らず、そ
の行からの文字列が対象になります。

1回目の検索対象(ファイル先頭から開始)
xx
s
g
e
g
y
g
s
yy
(略)


2回目の検索対象(gから開始)
g
e
g
y
g
s
yy
(略)


(?#lookbehind)というのは、強調表示専用の指示で、前の行に遡るという効果は無く
て、行頭まで遡るという効果だけになります。
通常の検索の場合は(?#lookbehind)の効果は常に有効で、あっても無くても同じです。

検索開始位置から前の行に遡って下検索するという方法は無くて、正規表現だけで書
くのは無理そうです。
通常の検索でさらなる拡張できる記述を用意することも考えられますが、強調表示す
ることを想定されているとしたら、できるようになったとしても、遅くなって実用的
なものにはならないかもしれません。

なんとかするとしたら、マクロを使ってまずxx〜yyの範囲を調べてから検索するとか、
カラーマーカーを付けるとか、そういう方法しか無さそうです。
検索で範囲だけに絞る場合は、inselectやrangeeditinの方法がありますが、いずれ
も行単位です。
文字単位の場合、V8.93以下では、あらかじめ範囲をsettargetcolormarkerで指定し
てからやる方法があります。
V8.94β4では、もっと簡単にinselect2の方法があります。
あるいは、また何かにつけてcolormarkerallfoundの例を出してしまいますが、これ
のほうがやりやすいかもしれません。

カラーマーカーのマクロの例:
setcompatiblemode 0x20000;
escape;
gofiletop;
searchdown "xx";
if(result!=false){
    #c1=column;
    #l1=lineno;
    searchdown "yy";
    if(result!=false){
        escape;
        #c2=column;
        #l2=lineno;
        setsearch "g",0;
        colormarkerallfound 0xff,0xff0000,-1,0,0,findmarker,#l1,#c1,#l2,#c2;
    }
}

[ ]
RE:10225 行を跨いでの前方/後方一致指定No.10226
fzok4234 さん 20/07/20 16:43
 
アドバイスありがとうございます。

実はあの正規表現パターンは、初めからマクロのcolormarkerallfoundで特定のブロ
ック(クラスや関数など)の中の
特定のステートメントだけを強調表示させる意図のものです。

元は、次のようなマクロで正規表現による強調表示を定義していました。


// -------------------------------- hilabc.mac --------------------------------

disablebreak    ;
setcompatiblemode   (
    0x00020000  |
    0x00800000
   
)               ;
disabledraw     ;
disableinvert   ;

// 検索対象の定義。
$tokenDelimiter =   @"\s+"      ;   // 字句の境界は改行、スペースまたはタブ
が必ず存在する。
$methodBegin    =   @"xx"       ;   // 仮想的な「関数」の開始字句。
$methodEnd      =   @"yy"       ;   // 仮想的な「関数」の終了字句。
$statement      =   @"[a-z]"    ;   // 関数内の仮想的な「文」の字句。
$target         =   @"g"        ;   // 強調表示させる対象の文。

// 正規表現パターンを組み立てる。HmJre.dll専用。
$searchPattern  =   (
    // 正規表現オプション。
    (
        @"(?#lookbehind)"       +
        @"(?#maxlines:50)"      +
        @"(?#fulllinematch)"
       
    )   +
   
    // 検索パターン本文。
    (
        // 前方一致の条件。
        (   @"(?<=" +
            // まず関数の開始がある。
            (
                @"("            +
                $methodBegin    +
                @")"
               
            )   +
           
            // その後に文が任意個数続く。
            (
                @"("            +
                $tokenDelimiter +
                @"("            +
                $statement      +
                @"))*"
               
            )   +
           
            //前の文と強調表示させる文との境界。
            $tokenDelimiter +
           
        @")"    )   +
       
        // 強調表示させる文。
        (
            @"("    +
            $target +
            @")"
           
        )   +
       
        // 後方一致の条件。
        (   @"(?="  +
            // 強調表示させる文と後の文との境界。
            $tokenDelimiter +
           
            // 文が任意個数続く。
            (
                @"(("           +
                $statement      +
                @")"            +
                $tokenDelimiter +
                @")*"
               
            )   +
           
            // 関数の終了。
            (
                @"("        +
                $methodEnd  +  
                @")"
               
            )   +
           
        @")"    )
       
    )
   
)   ;

setsearch
    $searchPattern      ,
    (
        0x00000002  |
        0x00000010
       
    )
   
;

#userData   =   1       ;
$layer      =   @"test" ;

// 強調表示の範囲はユーザーに指定させる。
#startLineno    =   val(
    input(
        @"開始行"   ,
        @""
       
    )
   
)                       ;
#startColumn    =   0   ;
#endLineno      =   val(
    input(
        @"終了行"   ,
        @""
       
    )
   
)                       ;
#endColumn      =   0   ;

deletecolormarker
    $layer          ,
    #userData       ,
    #startLineno    ,
    #startColumn    ,
    #endLineno      ,
    #endColumn
   
;
colormarkerallfound
    -1                  ,
    -1                  ,
    -1                  ,
    (
        0x00040000  |       // 強調表示1を適用。
        0x00000010
       
    )                   ,
    #userData           ,
    $layer              ,
    #startLineno        ,
    #startColumn        ,
    #endLineno          ,
    #endColumn
   
;

enableinvert    ;
enabledraw      ;
redraw          ;
enablebreak     ;

endmacro        ;

// -------------------------------- hilabc.mac --------------------------------


そして、次のテキストに対して強調表示を行おうとしました。


// -------------------------------- abc.txt --------------------------------

// 文「g」が強調表示される。

// 1個目の強調表示対象の関数。
xx
    w
    g
    s
   
    g
    e
    g
   
    y
    i
    g
   
yy

// 強調表示の対象外。
ww
    w
    g
    s
   
    g
    e
    g
   
    y
    i
    g
   
zz

// 2個目の強調表示対象の関数。
xx
    w
    u
    g
   
    a
    g
    b
   
    g
    i
    s
   
yy

// -------------------------------- abc.txt --------------------------------


しかし、実際に開始行を1、終了行を50にして実行したところ、2個目の関数の中の一
番上にある「g」だけが強調表示されるだけと
なってしまったため、検索ダイアログで同じような正規表現で検索をかけてもやはり
同じ「g」だけがヒットする状態となりました。
それから少し条件を変更(\s+を\s*)に変えたものが質問の検索パターンです。


> とりあえず、[a-z]は改行は含まないので、改行を含ませるとしたら以下のような
>感じになると思います。
> (?#lookbehind)(?#maxlines:50)(?#fulllinematch)(?<=(xx\s*)([a-z]\s*|\n)*)
>(g)(?=(\s*[a-z]|\n)*(\s*yy))

確かに、こちらで検索したら最初の「g」にヒットしました。ただ、\sにはもともと
タブやスペースに加えて改行も入っているので、
なぜ明示的に\nを入れたら意図したとおりに動いたのかよくわかりません。


> ただこれだとしても、ファイル先頭から下検索を開始したときに、最初のgにヒッ
>トするだけになってしまいます。
> gの位置から下検索を開始したときは、検索の対象となる文字列は前には遡らず、
>その行からの文字列が対象になります。

もしおっしゃる通りならば、1個目の関数の中の一番上にある「g」だけがヒットする
はずですが、実際にはこれではなく2個目の関数の
ほうでヒットしているので、やはり秀丸エディタかHmJre.dllのほうで何か問題があ
るのではと疑ってしまいます。



[ ]
RE:10226 行を跨いでの前方/後方一致指定No.10227
秀丸担当 さん 20/07/20 17:08
 

既にcolormarkerallfoundを使ったマクロだったのですね。

改行を含むかどうかについて、1つ勘違いがありました。
\sは改行を含むので、前方一致、後方一致については改行の判断はありました。
失礼しました。

確かにサンプルでは2回目のxx〜yyのほうがヒットしました。
以下のサンプルテキストのように外にgがあるところから検索開始すると、なぜかヒ
ットするようでした。

    g
xx
    g
yy

ちょっと複雑で、どこかに問題がありそうです。
もう少し調べてみます。

現状でやるとしたら、xx〜yyに範囲を絞った単純なgの検索だとやりやすいと思います。

[ ]
RE:10226 行を跨いでの前方/後方一致指定No.10228
秀丸担当 さん 20/07/21 09:10
 

2回目のxx〜yyのほうがヒットする問題を調べてみて、HmJreに問題がありました。
すみません。
前方一致の中に改行も含んだ繰り返しパターンがある場合に問題でした。
今後のバージョンで修正させていただきます。

現状で回避する方法として、(?<=)の中に\nという記述が直接あると回避できました。
簡略にした例:
(?#maxlines:50)(?#fulllinematch)(?<=xx(.|\n)*)g(?=(.|\n)*yy)

他には、(?<=)と(?=)は使わずに、(?\番号)のタグの番号で指定する書き方だと回避
できました。
簡略にした例:
(?#maxlines:50)(?#fulllinematch)(xx.*)(g)(.*yy)(?\2)
でもこの場合は、単一行のxx〜yy内にあるgは1つしかヒットしないので完全な代替
にはならないかもしれません。

ただHmJreとして回避できたとしても、秀丸エディタの検索開始位置以降の文字列が
対象という制約によって、どのみち複数行のxx〜yy内にある最初のgしかヒットしな
いです。
やはり、xx〜yyに範囲を絞った単純なgだけの検索するしかなさそうです。

[ ]
RE:10228 行を跨いでの前方/後方一致指定No.10229
fzok4234 さん 20/07/21 10:00
 
調査ありがとうございます。

秀丸エディタ本体の方には問題ないということで、当面はサードパーティーの正規表
現エンジンの使用でしのぐことになりそうです。


> ただHmJreとして回避できたとしても、秀丸エディタの検索開始位置以降の文字列
>が対象という制約によって、どのみち複数行のxx〜yy内に
> ある最初のgしかヒットしないです。
> やはり、xx〜yyに範囲を絞った単純なgだけの検索するしかなさそうです。

ただ、この方法だとxxとyyの正しいペアを識別するのに苦労しそうです。xx〜yyのブ
ロック中に入れ子でxx〜yyブロックとそうでない
ブロック(ww〜zzとか)が混在してあるときとかでは、HmJre.dllで上の行から順に検
索するやり方ではまず不可能とみられます。この場合は、
部分式を再帰で呼び出し可能な「鬼雲」系の正規表現エンジンを使用しないといけな
いみたいです。


[ ]