2014年10月22日水曜日

[VBA] RegExp.Replace で改行コードが変わってしまう

RegExp.Replace メソッドで置換したら、改行コードが CrLf から Lf になってしまう問題があります。これは行末 "$" までの置換を行った場合に起こる。

例えば、SQLのコメントを全て削るような置換パターンで発生する。
RegExp.Pattern = "--.+$"
RegExp.Replace(SQL, "")


このとき、実際は改行コード Cr まで含めた置換になっており、問題が発生します。
(置換前)ColName -- コメントCrLf    ※赤字が置換される部分
(置換後)ColName Lf
改行コード CrLf のうち、Cr が置換され消えてしまい Lf だけが残るので、改行コード CrLfLf に変換されたように感じられます。

.+(ドット プラス)や .*(ドット アアスタリスク)などの任意の文字の繰り返しパターンが問題になる。ドットは「改行文字を含まない任意の 1 文字」を表すので、リファレンスによれば、どうやら仕様通りとも言えるのだが・・・
  • $ : 入力文字列の末尾と一致します。RegExp オブジェクトの Multiline プロパティが設定されている場合、$'\n' または '\r' の直前にも一致します。
    → つまり、'\n' の直前の '\r' は一致してしまうのね。
  • .(ドット) : 改行文字 "\n" を除く任意の 1 文字に一致します。
    → つまり、キャリッジ リターン "\r" は除いていないのね。

いやいや!この仕様は無理がある。

ドットでない特定のパターンの繰り返しならば、改行コードが削られることは無い。
例えば、行末のスペースとタブのゴミを削るパターン
RegExp.Pattern = "( |\t)+$"
RegExp.Replace(SQL, "")


検証用のサンプルロジック

Const Sample As String = _
    "abc -- コメント" & vbCrLf & _
    "def" & vbCrLf & _
    "ghi"

Dim Reg As New RegExp
Reg.Global = True
Reg.MultiLine = True
Reg.Pattern = "--.+$"

Dim Text As String
Text = Reg.Replace(Sample, "")
For i = 1 To Len(Text)
    Debug.Print Mid(Text, i, 1) & vbTab & Asc(Mid(Text, i, 1))
Next

■ 参考資料

正規表現の構文
http://msdn.microsoft.com/ja-jp/library/cc392020.aspx