4

正規表現の謎を調査しています。私は疲れているので、明らかな何かが欠けているかもしれませんが、その理由はわかりません。

以下の例では、perlを使用していますが、これはVIMで最初に見たので、複数の正規表現エンジンに関連していると思います。

このファイルがあると仮定します。

$ cat data
1 =2   3 =4
5 =6  7 =8

次に、「=」の前の空白を削除できます。

$ cat data | perl -ne 's,(.)\s+=(.),\1=\2,g; print;'
1=2   3=4
5=6  7=8

すべての行で、一致のすべてのインスタンスが置き換えられていることに注意してください。/ g検索修飾子を使用しました。これは最初の置換で停止せず、代わりに行の終わりまで置換を続けます。

たとえば、「=2」の前のスペースと「=4」の前のスペースの両方が削除されました。同じ行に

's、=、=、g'のような単純な構造を使用してみませんか?さて、私たちはもっと難しいシナリオの準備をしていました...割り当ての右側が引用符で囲まれた文字列であり、一重引用符または二重引用符のいずれかである可能性があります。

$ cat data2
1 ="2"   3 ='4 ='
5 ='6'  7 ="8"

同じ作業(等号の前の空白を削除)を行うには、文字列に等号が含まれている可能性があるため、注意する必要があります。したがって、最初に表示される引用符にマークを付け、後方参照を介してそれを探します。

$ cat data2 | perl -ne 's,(.)\s+=(.)([^\2]*)\2,\1=\2\3\2,g; print;'
1="2"   3='4 ='
5='6'  7="8"

後方参照\2を使用して、最初に見たものと同じ引用符ではないものを何度でも検索しました([^ \ 2] *)。次に、元の見積もり自体(\ 2)を検索しました。見つかった場合は、逆参照を使用して、置換ターゲット内の一致したパーツを参照しました。

今これを見てください:

$ cat data3 
posAndWidth ="40:5 ="   height        ="1"
posAndWidth ="-1:8 ='"  textAlignment ="Right"

ここで必要なのは、すべての行で「=」のすべてのインスタンスの前に存在する最後のスペース文字を削除することです。以前と同様に、文字列自体に等号が含まれている可能性があるため、単純な's、= "、="、g'を使用することはできません。

したがって、上記と同じパターンに従い、後方参照を使用します。

$ cat data3 | perl -ne "s,(\w+)(\s*) =(['\"])([^\3]*)\3,\1\2=\3\4\3,g; print;"
posAndWidth="40:5 ="   height        ="1"
posAndWidth="-1:8 ='"  textAlignment ="Right"

それは機能します...しかし、ラインの最初の試合でのみ!'textAlignment'に続くスペースは削除されず、その上にあるスペース('height'のもの)も削除されませんでした。

基本的に、/ gは機能しなくなったようです。/gなしで同じreplaceコマンドを実行すると、まったく同じ出力が生成されます。

$ cat data3 | perl -ne "s,(\w+)(\s*) =(['\"])([^\3]*)\3,\1\2=\3\4\3,; print;"
posAndWidth="40:5 ="   height        ="1"
posAndWidth="-1:8 ='"  textAlignment ="Right"

この正規表現では、/gは無視されているようです。なぜ何かアイデアはありますか?

4

2 に答える 2

3

置換にいくつかのデバッグ文字を挿入すると、問題が明らかになります。

use strict;
use warnings;

while (<DATA>) {
    s,(\w+)(\s*) =(['"])([^\3]*)\3,$1$2=$3<$4>$3,g;
    print;                       #  here -^ -^
}

__DATA__
posAndWidth ="40:5 ="   height        ="1"
posAndWidth ="-1:8 ='"  textAlignment ="Right"

出力:

posAndWidth="<40:5 ="   height        ="1>"
posAndWidth="<-1:8 ='"  textAlignment ="Right>"
#            ^--------- match ---------------^

一致は両方の引用符を同時に通過することに注意してください。それは[^\3]*あなたが思っていることをしていないように思われるでしょう。

正規表現はここでは最高のツールではありません。次のような引用符で囲まれた文字列を処理できるパーサーを使用しますText::ParseWords

use strict;
use warnings;
use Data::Dumper;
use Text::ParseWords;

while (<DATA>) {
    chomp;
    my @a = quotewords('\s+', 1, $_);
    print Dumper \@a;
    print "@a\n";
}

__DATA__
posAndWidth ="40:5 ="   height        ="1"
posAndWidth ="-1:8 ='"  textAlignment ="Right"

出力:

$VAR1 = [
          'posAndWidth',
          '="40:5 ="',
          'height',
          '="1"'
        ];
posAndWidth ="40:5 =" height ="1"
$VAR1 = [
          'posAndWidth',
          '="-1:8 =\'"',
          'textAlignment',
          '="Right"'
        ];
posAndWidth ="-1:8 ='" textAlignment ="Right"

文字列がどのように分割されているかを確認できるように、Dumper出力を含めました。

于 2013-03-08T15:18:26.563 に答える
1

TLPの回答に対する私のコメントについて詳しく説明します。

あなたが2つの質問をしているttsiodras:

1-正規表現で目的の結果が得られないのはなぜですか?なぜgフラグが機能しないのですか?

答えは、正規表現に[^\3]正しく処理されないこの部分が含まれているため\3です。後方参照として認識されません。私はそれを探しましたが、文字クラスで後方参照を持つ方法を見つけることができませんでした。

2-等号の前のスペースを削除し、引用符の後に続く部分をそのままにしておくにはどうすればよいですか?

これはそれを行う方法です(このリファレンスを参照):

$ cat data3 | perl -pe "s,(([\"']).*?\2)| (=),\1\3,g"
posAndWidth="40:5 ="   height       ="1"
posAndWidth="-1:8 ='"  textAlignment="Right"

正規表現の最初の部分は、引用符(シングルまたはダブル)の間にあるものをすべてキャッチし、一致に置き換えられます。2番目の部分は、探しているスペースが前に付いた等号に対応します。 この解決策は、欲張りでない演算子を使用した後方参照を使用した補完文字クラス演算子に関する「興味深い」部分の回避策にすぎないことに注意してください。[^\3]*?


最後に、ネガティブな先読みソリューションを追求したい場合:

$ cat data3 | perl -pe 's,(\w+)(\s*) =(["'"'"'])((?:(?!\3).)*)\3,\1\2=\3\4\3,g'
posAndWidth="40:5 ="   height       ="1"
posAndWidth="-1:8 ='"  textAlignment="Right"

角かっこで囲まれた部分はまだ意味"[\"']"がありますが、perlコマンド全体を一重引用符で囲む必要がありました。そうしないと、負の先読み(?!...)構文がbashでエラーを返します。

編集負の先読みで正規表現を修正しました:貪欲でない演算子とフラグ*?に再度注意してください。g

編集ttsiodrasのコメントを考慮に入れました:貪欲でない演算子を削除しました。

編集TLPのコメントを考慮に入れました

于 2013-03-08T23:04:44.667 に答える