2007-02 / 2007-02-09

前のエントリ: エスケープされていないダブルクォート [正規表現]
次のエントリ: 置換時に式を評価する [正規表現]

前回のマッチ位置から処理する
2007-02-09-2 / カテゴリ: [perl][正規表現] / [permlink]

単に繰り返したい場合は
/regexp/g;
/g オプションで、繰り返し処理される。
けど、ある正規表現にマッチした場合、その内容によって以降の処理が変化させたい、などのちょっと複雑(?)な場合は、/c /g によって、マッチ箇所を記憶させ、次に \G でその位置を参照できる。

# 複数行にまたがっていないコメント・文字列の削除
$r_dq = qr/(?<!\\)(?:\\\\)*\"/;
while (<>) {
  while (/(['#]|$r_dq)/cg) {
    # コメントやらリテラルやらの削除

    if ($1 eq "'") {
      # シングルクォートのリテラル削除
      s/.\G.*?\'//;
    }
    elsif ($1 eq '"') {
      # ダブルクォートのリテラル削除
      s/.\G.*?$r_dq//g;
    }
    elsif ($1 eq '#') {
      # コメント削除
      s/.\G.*$//;
    }
  }
  print;
}
2重目の while の条件式で、まずシングルクォート、コメント、ダブルクォートを検知し、$1 の中身によって処理振り分け。
次の正規表現でシングルクォート・ダブルクォートであれば次のクォートまで削除。コメントであれば行末まで削除。
\G の手前に . を入れてるのは、初めの正規表現でマッチした文字(' or " or #)自身。
という処理を while により繰り返す。


シェルスクリプトのコードチェックスクリプトを作成する過程で、1. コメントを削除、2. 文字列を削除 すればいーかなと思ったが、クォート内の # 以降を削ると、閉じクォートが消えてしまう。
逆にクォートを先にやると、コメント内に閉じてないクォートがある場合におかしくなってしまう。
ってことで同時にやらなきゃいけないっぽい。


複数行・ヒアドキュメント対応版
$g_here = "";
$g_nest = "";
$r_dq = qr/(?<!\\)(?:\\\\)*\"/;

while (<>) {
  if ($g_nest) {
    if (s/.*$g_nest//) {
      # 閉じられた
      print "        $g_nest is closed (line $.)\n";
      $g_nest = "";
    }
    else {
      # まだ閉じてない
      next;
    }
  }
  # ヒアドキュメント中?
  if ($g_here) {
    $g_here = "" if /^$g_here$/;
    next;
  }

  while (/(['#]|$r_dq)/cg) {
    # コメントやらリテラルやらの削除

    if ($1 eq "'") {
      # シングルクォートのリテラル削除
      s/.\G.*?\'//g;
    }
    elsif ($1 eq '"') {
      # ダブルクォートのリテラル削除
      s/.\G.*?$r_dq//g;
    }
    elsif ($1 eq '#') {
      # コメント削除
      s/.\G.*$//g;
    }
  }

  # クォートの閉じ忘れチェック
  if (s/('|$r_dq).*//) {
    $g_nest = $1;
    print "warn? : $g_nest is not closed (line: $.)\n";
  }
  # ヒアドキュメントチェック
  $g_here = $1 if /<<\s*(\w+)/;

  print;
}
全行を1変数に入れて /ms で一気にやれば楽だけど、行番号も欲しいからなぁ。


コメント・行番号無視なら
use Regexp::Common;

s/$RE{quoted//g;
で、クォートされてる文字列は消せる。
前のエントリ: エスケープされていないダブルクォート [正規表現]
次のエントリ: 置換時に式を評価する [正規表現]

2013 : 01 02 03 04 05 06 07 08 09 10 11 12
2012 : 01 02 03 04 05 06 07 08 09 10 11 12
2011 : 01 02 03 04 05 06 07 08 09 10 11 12
2010 : 01 02 03 04 05 06 07 08 09 10 11 12
2009 : 01 02 03 04 05 06 07 08 09 10 11 12
2008 : 01 02 03 04 05 06 07 08 09 10 11 12
2007 : 01 02 03 04 05 06 07 08 09 10 11 12
2006 : 01 02 03 04 05 06 07 08 09 10 11 12
2005 : 01 02 03 04 05 06 07 08 09 10 11 12
2004 : 01 02 03 04 05 06 07 08 09 10 11 12

最終更新時間: 2013-05-02 16:12