質問: こんにちは。大学生です。 ダブルクオートで囲まれていない範囲で置換をしようとして困っています。 一度の置換でなくて構いません。 vimで置換をしていまして、ソースコード中の/の前後に半角空白を入れる置換をしようとしています。 例としては「a/b」を「a / b」のようにしたいです。 以下、やってみたことです。 pythonにある切り捨て除算//は a`//`b を前もって別の文字列にして、 この処理の後にa`//`bにする処理を入れます。 調べたところ「あいaうえお」を「あいbうえお」に置換するには `:%s/\(.*)a\(.*)/\1b\2/g` のようにすると良いとわかりました。 すでに前後に半角空白を実行ごとに増やさないようにしつつ、 いまこのような置換コマンドを作りました。 `:%s;\([^\s/]\)\s*/\s*\([^\s/]\);\1 / \2;ge` わかりづらいので改行を入れますと、 `:%s` ファイル全体を置換 `;` 区切りを;にする よくある`:%s/a/b/g`を`:%s;f;b;g`に `\([^\s/]\)` グループ\1 スラッシュと空白以外の文字 `\s*` スラッシュの前にある0個以上の空白 `/` 目的のスラッシュ `\s*` スラッシュの前にある0個以上の空白 `\([^\s/]\)` グループ\2 スラッシュと空白以外の文字 `;` 置換前と置換後の区切り文字 `\1 / \2` グループ1と2で_/_を挟む `;ge` もしマッチしなくてもエラーを表示しない しかしソースコード中にダブルクォートで囲まれたパスが存在するため、 このままでは「"/home/user/a.png"」が「" / home / user / a.png"」のようになってしまいます。 このため””で囲まれていない範囲で置換を行いたいです。 (シングルクォートはこの際無視します_(┐「ε:)_) コードフォーマッタを使うといいと思われると思いますが、 ソースコードはCやPython,Haskellなど複数種類あり、 都合の良いコードフォーマッタがありません。 そのためvimのコマンドとして関数を定義し、 .vimrcに書き、必要な時に呼び出したいと思います。 ある程度の副作用はその都度コマンドを修正していき、 最終的には僕の考えた最強の/を置換するコマンド、 という微妙なものを作りたいと思います。 調べたところ、条件が異なる回答ではありますが 「かっこ[]で囲まれた文字以外をマッチさせたい」という質問の回答で > > 先読みを利用すれば出来ると思います。 > > マッチさせる箇所(A)を`[^\[\]]+`、させない箇所(B)を`\[[^\[\]]+\]`だとすると、先読みさせるにはA(?=B)、つまり`[^\[\]]+(?=\[[^\[\]]+\])`という形になります。これと末尾(もしくは全体)にマッチするパターン`A$=[^\[\]]+$`を組み合わせれば > > `([^\[\]]+(?=\[[^\[\]]+\])|[^\[\]]+$)` > > となります。 > > > > > というのを見つけ、これを使えばうまくできるのではないかと考えています。先読みというのが初めてでvimの正規表現では を見て、条件となるパターン1とマッチさせたいパターン2の2つを用いてパターンマッチさせる方法であると理解しました。 (自信はありません_(┐「ε:)_) 私の場合、1でない全ての2を置換後文字列3にする否定先読み、否定後読みを用いるのが良いと考えました。 この場合、1条件となるパターンと、2マッチさせたいパターン、3置換後文字列は 1「""」で囲まれている文字列 2「/」 すでに前後に空白がある時はそれも含む 3「/の前の文字列」半角空白/半角空白「/の後の文字列」 となるのでしょうか。 ここから正規表現にするところで止まっています。 正規表現でダメだったら、1行読み込んで"があるかどうか判定、 "がなければ前述の置換、あれば"までの文字列と”の中の文字列、 "の後の文字列に分けて""以外の文字列に置換、というのを書こうと思います_(┐「ε:)_
回答: `"` が入れ子にならないという条件の元であれば、sedやperlのような正規表現の扱いを得意とする外部プログラムに投げればいいんじゃないでしょうか。 ``` :%!perl -pe 's@("[^"]*"|[^"/]+)|/@$1//" / "@ge' ``` 「入れ子も正規表現で解決しよう」というのは無しね。それは不可能。