[ 新規に投稿する ]

行を跨いでの前方/後方一致指定での検索の仕方について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で上の行から順に検索するやり方ではまず不可能とみられます。この場合は、
部分式を再帰で呼び出し可能な「鬼雲」系の正規表現エンジンを使用しないといけないみたいです。

[ ]

[ 新規に投稿する ]