web-dev-qa-db-ja.com

vim正規表現の検索と置換

ファイル内の文字列の一部を置き換えようとしています

たとえば、csvファイルがあります。

r1,col1,col2,35,000,col4,col5
r2,col1,col2,1,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4,325.33,col4,col5

基本的に、上記のcol3のように見えるものを置き換えたいと思います。最初のx番号を保持しながら、次のように表示されます。

r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5

通常は実行します

:%s/\,[0-9]*\,/\,\1/g

しかし、私がそれを実行すると、

r1,col1,col2,000,col4,col5
r2,col1,col2,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,325.33,col4,col5

目的の出力が得られるように、置換の2番目の部分で何を使用する必要がありますか。

2
Mark D

あなたおそらく:%s/\v(([^,]*,){3})([0-9]+),([0-9])/\1\3\4/が欲しいです。

目標は、他の場所でコンマを削除したり、他のテキストを削除したりせずに、4番目のフィールド(存在する場合)からコンマを削除することです。複雑な要因は、コンマがフィールド区切り文字としても使用されることです。この問題を解決するには、フィールド内にコンマが表示される条件について知っていることを考慮する必要があります。結局のところ、それ以上の制約がなければ、レコードはあいまいです。

2桁にまたがるコンマを削除の対象と見なしたくなりますが、これは機能しません。サンプル入力は、canの1つのフィールドが数字で終わり、次のフィールドが1(col2,35,000)で始まることを示しています。

最初の3つのフィールド自体にコンマが含まれていないことがわかっている場合は、問題がはるかに簡単になります。これは、ゼロ以上の非コンマとそれに続くコンマの最初の3つのシーケンスをスキップしてから、コンマを削除できるためです。次に、4番目のフィールドがいつ終了したかをどのように把握するかが問題になります。 4番目のフィールドから複数のコンマを削除するか、それとも常にコンマがないか1つのコンマであるかを自問する必要があります。

この回答のために、4番目のフィールドには削除する必要のあるコンマが最大で1つ含まれていると仮定します。さらに、コンマは1つ以上の数字の後、少なくとも1つの数字の前に表示されると想定します。次に、これをVimで使用できます。

:%s/\v(([^,]*,){3})([0-9]+),([0-9])/\1\3\4/

または、Sedを使用したい場合:

sed -r 's/(([^,]*,){3})([0-9]+),([0-9])/\1\3\4/' filename.csv

使い方

正規表現(([^,]*,){3})は、最初の3つのフィールドとそれに続くフィールド区切り文字に一致します。これらはすべて、同じままにする必要があります。 [^,]は、,以外の任意の1文字に一致します。後の*により、正確に1つではなく0個以上が一致します。その後の,は、この非コンマのフィールドに続く実際のコンマと一致します。これはすべて()でグループ化され、{3}が適用されると、1回ではなく3回一致します。次に、that全体がグループ化され、\1でアクセスできるようになります。 (内部グループもキャプチャし、\2としてアクセスできます。)

次に、([0-9]+)は1つ以上の(+)桁([0-9])と一致し、一致(())をキャプチャして、\3としてアクセスできるようにします。 ,文字はリテラルコンマと一致します。これは私たちが保持しない部分です。次に、([0-9])は1桁をキャプチャして、\4としてアクセスできるようにします。

\1\3に単一のグループ、つまり(([^,]*,){3}[0-9]+)を使用することで、正規表現を少し簡単にすることができます。レコードの構造(コンマで区切られたフィールドで構成されている)が隠されていると感じるので、それを避けましたが、そのようにすることには何の問題もありません。これを行うと、\4\3になるため、置換パターンでは、\1\3の代わりに\1\3\4を使用します。

最後に、 \v Vim正規表現の開始時に、sedに渡される-rは、拡張正規表現構文を使用できるようにします。そのため、()の代わりに\(\)を記述し、+の代わりに\+を記述できました。

1
Eliah Kagan

次の正規表現を使用して、vim内から実行できます。

%s/\([^,]\+,\)\{3}[^,]*\zs,\ze[^,]*\(,[^,]\+\)\{2}//

ここに説明があります:

  • \([^,]\+,\)\{3}は、3つのcsvフィールドとそれに続くコンマに正確に一致します。

  • \(,[^,]\+\)\{2}は、2つのcsvフィールドとその前のコンマに正確に一致します。

  • これらの2つの式の真ん中にあるものは、1つのコンマを削除する必要があるフィールドをキャプチャします。

1
Rastapopoulos

このタスクにはawkを使用できます。このスクリプトは、4番目の列の複数のコンマを処理できます。 vimを使用してこのケース(複数のコンマ)を処理することは難しいと思います。ただし、awkを使用すると簡単です。

注:このソリューションは6列のみです(私もr1列を数えています)。

awk '
BEGIN {
    FS = ",";
    OFS = ",";
}
{
    accum = "";
    for(i = 4; i < NF - 1; i++) {
        accum = accum $i;       
    }

    print $1, $2, $3, accum, $(NF - 1), $NF;
}' input.txt

入力(ターゲットフィールドに複数のコンマが含まれる行がテスト用に追加されました)

r1,col1,col2,35,000,col4,col5
r2,col1,col2,1,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4,325.33,col4,col5
r5,col1,col2,4,325,250.33,col4,col5
r6,col1,col2,4,100,325,250.33,col4,col5

出力

r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5
r5,col1,col2,4325250.33,col4,col5
r6,col1,col2,4100325250.33,col4,col5
0
MiniMax
$ sed 's/,\([0-9]\+\),\([0-9]\+\)/,\1\2/' input
r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5

使用される表現を説明するには:

  NODE                     EXPLANATION
  ,                        ','
  (                        group and capture to \1:
    [0-9]+                   any character of: '0' to '9' (1 or more
                             times (matching the most amount
                             possible))
  )                        end of \1
  ,                        ','
  (                        group and capture to \2:
    [0-9]+                   any character of: '0' to '9' (1 or more
                             times (matching the most amount
                             possible))
  )                        end of \2

次に、一致を,\1\2に置き換えます。

0
DopeGhoti