言語のエスケープ文字と正規表現のリテラル表現(エスケープ文字)について

UWSC公式掲示板に以下のような質問があった。

正規表現置換をしたかったのですが上手く動きません。
csvを読み込んでtsvに変換するため、
正規表現の置換結果に\tを使いたいのですが、
"\t"がそのまま書かれてしまいます。
いまはReplace文で通常置換してますが、もやもやするので
どなたか添削をお願いします。

srcfile = "c:\hoge.csv" // テスト用ファイル名

FCSV = Fopen( srcfile, F_READ)
本文 = Fget( FCSV, F_ALLTEXT )

本文 = ReplaceReg( 本文, ",", "\t" ) // csvをtsvに置換

msgbox( 本文 )

Function ReplaceReg(targetText, patternText, repText)
        regex = CreateOleObj("VBScript.RegExp")
        regex.Global = True
        regex.IgnoreCase = True
        regex.Pattern = patternText
        Result = regex.Replace(targetText, repText)
Fend

なんとなくちゃちゃっと回答して、混乱させてしまったようなので、記事にしました。



経緯

私の回答は

本文 = ReplaceReg( 本文, ",", "<#TAB>" )

javascript等で置き換え文字を「\t」と書くのは、javascript自身のエスケープシーケンスによるものです。
正規表現によるものではありません。
UWSCにおいては、タブは「<#TAB>」かCHR関数で生成します。

これにさらなる質問が

\t等の表現は正規表現のルール内では無く、正規表現とは別の、
環境依存のローカルなルールという事なのでしょうか?

uwscから少し離れてしまいますが、かなり混乱してしまったので
出来れば正しいところをお教えください。

正規表現は\n \r \t \x00等の置き換え表現が原則で、バイナリ値を
 直接書いて正常動作を期待するのが環境依存なんだと思ってましたが、
 これが逆と言うことでしょうか?※<#tab>は実行時はバイナリ値ですよね?
・\tのみ特殊と言うことでしょうか?
・検索文字列は\t \r \n等が通用するが置換結果側の文字列には\t\r\n 等は使えない?
・\t\r\nはプログラム実行時にバイナリ値に変換されてから正規表現処理に投げ込まれている?
()や[]は正規表現が処理してるが\t\r\n等は正規表現とは無関係に独立していると言うこと?
・\tと<#tab>は完全に等価と言うことでしょうか?
 (どちらも正規表現ではなく、言語の規則がバイナリ値に変換してる?)

等々、ご返信をみてから疑問が尽きません。
再度の問い合わせで申し訳ありませんが、出来ましたらお教え頂けるか、
わかりやすい解説ページなどあれば是非お願いします。

軽い質問のつもりが結果に呆然としてます…

まずは、さらなる質問に対する回答

以下は全て、UWSCにおいての話です。

\t等の表現は正規表現のルール内では無く、正規表現とは別の、
環境依存のローカルなルールという事なのでしょうか?

「\t」は「\t」です。
regex.Replaceに渡す二つの引数においては、「\t」は「\t」のままです。
ただし、regex.Patternに設定する文字列には、正規表現のルールが適用されます。
http://msdn.microsoft.com/ja-jp/library/ms974570.aspx
リテラルにあるように、「\t」は水平タブにマッチするものとして解釈されます。

正規表現は\n \r \t \x00等の置き換え表現が原則で、バイナリ値を
 直接書いて正常動作を期待するのが環境依存なんだと思ってましたが、
 これが逆と言うことでしょうか?※<#tab>は実行時はバイナリ値ですよね?

その理解は正しいです。逆ではありません。
(ただし「環境依存」というか「正規表現エンジン依存」です)
前述の通り、正規表現のPatternに設定する文字列は、リテラル表現にするのが基本です。
古い正規表現の実装系の中には、ASCII文字しか受け付けず、日本語がダメなものも多かったです。
さて、バイナリってなんでしょうね?

・\tのみ特殊と言うことでしょうか?

前述の通り、違います。
\tが特殊ということはありません。
「\t」が水平タブだと思っている人がいるとすれば、それが間違いだと言いたいです。

・検索文字列は\t \r \n等が通用するが置換結果側の文字列には\t\r\n 等は使えない?

その通りですが、解釈を間違えているかと思います。
Patternに\t等があった場合、正規表現エンジンがその部分をCHR(9)に置き換えている、と考えているなら、その考えを改めた方が良いです。(実装は知らんけど)
「\t」という文字列はあくまで「\t」です。
正規表現エンジンが検索する際に「\t」と言われたら、CHR(9)を探すだけです。
ちょうど「[a-z]」と言われたら小文字を探すのと同じです。
同様にReplaceに渡す文字列も、「\t」は「\t」です。CHR(9)ではありません。

・\t\r\nはプログラム実行時にバイナリ値に変換されてから正規表現処理に投げ込まれている?
()や[]は正規表現が処理してるが\t\r\n等は正規表現とは無関係に独立していると言うこと?

前述の通り、Patternの\t等は正規表現エンジンが処理します。
変換という概念は、、、私にはありません。
「[a-z]」を小文字にマッチする何らかの記号に変換している、という解釈をしている人なら、\tを水平タブに変換していると考えて良いとは思います。

・\tと<#tab>は完全に等価と言うことでしょうか?
 (どちらも正規表現ではなく、言語の規則がバイナリ値に変換してる?)

前述の通り、等価ではありません。
Replaceに渡した場合は、「\t」とCHR(9)です。
が、Patternに渡した場合は、どちらもCHR(9)にマッチします。
(前者は水平タブのリテラル表現、後者は水平タブそのもの)




以下余談です。

javascriptを視野に入れると

・\tと<#tab>は完全に等価と言うことでしょうか?

javascriptの文字「\t」と、UWSCの「<#TAB>」はどちらも言語のエンジンがCHR(9)にするため、等価です。
javascriptにおいては、正規表現パターンを文字列にすると言語によるエスケープ文字の解釈も入るため、面白いことになります。
http://www.ajaxtower.jp/js/num/index2.html

'a'.match('a')

これは、「a」という文字に対し、正規表現「a」のマッチを見ています。もちろんマッチします。


同様に

'\t'.match('\t')

これは、水平タブに対し、水平タブのマッチを見ています。マッチします。

'\t'.match('\\t')

水平タブに対し、「\t」のマッチを見ています。水平タブのリテラル表現なので、マッチします。

'\t'.match('\\\t')

水平タブに対し、「\(水平タブ)」のマッチを見ています。
「\(水平タブ)」は正規表現エンジンが水平タブにみなすので、マッチします!

'\t'.match('\\\\t')

水平タブに対し、「\\t」のマッチを見ています。
「\\t」は正規表現エンジンが「\t」にみなすので、マッチしません。


なお、文字列ではなく正規表現用の表記を用いると、こんな混乱はありません。

'\t'.match(/\t/)        // ok
'\t'.match(/\\t/)       // ng
'\t'.match(/\\\t/)      // ng
'\t'.match(/\\\\t/)     // ng


水平タブにマッチさせる推奨の書き方は

'\t'.match(/\t/)
'\t'.match('\\t')

であり、推奨ではないけど実質問題ないのは、

'\t'.match('\t')

です。

バイナリーって?

私は「バイナリ」って単語を使わないようにしました。
バイナリーがなんなのか、不明だからです。
いや、なんとなーく、わかりますよ。
でも、曖昧なのです。

a

これは文字ですね。
安心して言えます。

(改行)

これは、、、文字ですか?
文字ではなく「バイナリ」とやら?
ちなみに、実体はなんでしょう?
CHR(13)+CHR(10)? // Windows
CHR(13)? // Mac
CHR(10)? // Linux
どれでしょう?

これは、、、文字?
日本語を知らない人が見たら、バイナリーですよ、多分。
ちなみに実体は?
CHRB(130)+CHRB(160)? // Shift-JIS
CHR(12354)? // UTF16

(水平タブ)

これは?


言語によって、文字列に使って良い文字があったりします。
この場合、文字列に入れてはいけないものがバイナリーと言えそうです。
そう定義すると、水平タブや改行は、文字な気がします。(大抵使えるから)


表示されるもの(表示を整形するものは除く)なら、水平タブや改行は入らない気がしますが、、、
日本語や中国語はどうなるでしょう?
英語圏ではバイナリー扱いされそうですね。


なお、私の中で文字列は

  • ASCII文字(0x20〜0x7e)と日本語、水平タブ・改行。終端に限りNULL文字

となっています。
文字列として扱って安全と思える範囲、というところです。
ただし、相手が日本語を知らない場合、「日本語」の範囲は除外されます。
また、日本語はコードに依存します(CP932,Shift-JIS,JIS,EBCDIC系,Unicode系,,,)。
改行も扱っている環境によります。
よってやはり不定です。