3

テキスト ファイルで文字列を検索し、一致するたびに増加する数値を含む置換を作成する必要があります。

「見つかった」文字列は、単一の文字、単語、またはフレーズである可能性があります。

置換式は (以下の例のように) 常に同じではありませんが、増分する数値 (変数) が常に含まれます。

例えば:

1) 「data.txt」という名前のテスト ファイルがあります。ファイルには次が含まれます。

Now is the time
for all good men
to come to the
aid of their party.

2) awk スクリプトを「cmd.awk」という名前のファイルに配置しました。ファイルには次が含まれます。

/f/ {sub ("f","f(" ++j ")")}1

3) awk を次のように使用します。

awk -f cmd.awk data.txt

この場合、出力は期待どおりです。

Now is the time
f(1)or all good men
to come to the
aid of(2) their party.

問題は、行に複数の一致がある場合に発生します。たとえば、次のような文字「i」を検索していたとします。

/i/ {sub ("i","i(" ++j ")")}1

出力は次のとおりです。

Now i(1)s the time
for all good men
to come to the
ai(2)d of their party.

「時間」または「彼ら」に「i」が含まれていないため、これは間違っています。

そこで、次のように「sub」の代わりに「gsub」を試しました。

/i/ {gsub ("i","i(" ++j ")")}1

出力は次のとおりです。

Now i(1)s the ti(1)me
for all good men
to come to the
ai(2)d of thei(2)r party.

今度は文字 "i" のすべての出現を置換しますが、挿入される数字は同じ行のすべての一致に対して同じです。

望ましい出力は次のようになります。

Now i(1)s the ti(2)me
for all good men
to come to the
ai(3)d of thei(4)r party.

注: 番号は常に「1」で始まるとは限らないため、次のように awk を使用する場合があります。

awk -f cmd.awk -v j=26 data.txt

出力を取得するには:

Now i(27)s the ti(28)me
for all good men
to come to the
ai(29)d of thei(30)r party.

明確にするために、置換の数値は常に括弧内にあるとは限りません。また、置換には常に一致した文字列が含まれるとは限りません (実際には非常にまれです)。

これで私が抱えている他の問題は...

「検索文字列」に awk 変数 (環境変数ではない) を使用したいので、awk コマンドラインで指定できます。

例えば:

1) awk スクリプトを「cmd.awk」という名前のファイルに配置しました。ファイルには次のようなものが含まれています。

/??a??/ {gsub (a,a "(" ++j ")")}1

2) awk を次のように使用します。

awk -f cmd.awk -v a=i data.txt

出力を取得するには:

Now i(1)s the ti(2)me
for all good men
to come to the
ai(3)d of thei(4)r party.

ここでの質問は、/search/ 式で変数 "a" をどのように表すかということです。

4

3 に答える 3

2

gensub()ここでは理想的に聞こえますが、N番目の一致を置き換えることができるため、解決策のように聞こえるのは、do{}while()一度に1つの一致を置き換えて、をインクリメントするループで文字列を反復処理することjです。この単純なgensub()アプローチは、置換に元のテキストが含まれていない場合(または、さらに悪いことに、複数回含まれている場合)は機能しません。以下を参照してください。

したがって、awkでは、perlの " s///e"評価機能とそのステートフル正規表現/g修飾子(Steveで使用)がないため、残りの最良のオプションは、行をチャンク(headmatchtail)に分割し、それらを再び貼り付けることです。

BEGIN { 
    if (j=="") j=1
    if (a=="") a="f"
}
match($0,a) { 
    str=$0; newstr=""
    do {
         newstr=newstr substr(str,1,RSTART-1) # head
         mm=substr(str,RSTART,RLENGTH)        # extract match
         sub(a,a"("j++")",mm)                 # replace
         newstr=newstr mm 
         str=substr(str,RSTART+RLENGTH)       # tail
    } while (match(str,a))
    $0=newstr str     
}
{print}

これはmatch()、パターンの代わりにepxressionとして// 使用するため、変数を使用できます。(「($0 ~ a) { ... }」を使用することもできますが、このコードではの結果match()が使用されているため、ここでは試さないでください。)

コマンドラインでjとを定義できます。a

gawkサポートはperlreと\y同等であり、単語の最初と最後を明示的に一致させるために、UNIXコマンドラインからエスケープを追加するように注意してください(Windowsが何を要求または許可するかはよくわかりません)。\b\<\>


限定gensub()

上で参照したように:

match($0,a) {
    idx=1; str=$0
    do {
        prev=str
        str=gensub(a,a"(" j ")",idx++,prev)
    } while (str!=prev && j++)
    $0=str
}

ここでの問題は次のとおりです。

  • iサブストリング" "をサブストリング" k"または" "に置き換えると、次の一致k(1)gensub()インデックスが1だけずれます。事前に知っている場合はこれを回避するか、代わりにストリングを逆方向に処理します。
  • iサブストリング" "をサブストリング" ii"または" "に置き換えるとii(i)、同様の問題が発生します(gensub()新しい一致を見つけ続けるため、無限ループが発生します)

両方の条件をしっかりと処理することは、コードの価値がありません。

于 2013-02-19T13:57:44.473 に答える
2

awkバージョン:

awk '{for(i=2; i<=NF; i++)$i="(" ++k ")" $i}1' FS=i OFS=i
于 2013-02-19T13:55:58.110 に答える
1

を使用してこれを実行できないと言っているわけではありませんがawk、より強力な言語に移行することを強くお勧めします。perl代わりに使用してください。

i26で始まる文字の数を含めるには、次のことを試してください。

perl -spe 's:i:$&."(".++$x.")":ge' -- -x=26 data.txt

これはシェル変数でもあります:

var=26
perl -spe 's:i:$&."(".++$x.")":ge' -- -x=$var data.txt

結果:

Now i(27)s the ti(28)me
for all good men
to come to the
ai(29)d of thei(30)r party.

\b特定の単語の数を含めるには、単語の周囲に単語の境界(つまり)を追加して、次のことを試してください。

perl -spe 's:\bthe\b:$&."(".++$x.")":ge' -- -x=5 data.txt

結果:

Now is the(6) time
for all good men
to come to the(7)
aid of their party.
于 2013-02-19T13:00:08.200 に答える