29740


VB.NETにて出力する文章に雛形を用意し、その雛形の # に番号を当てはめようとしました。

[code]

    <script runat="server" language="vb">
        function checkcode(s as String) as String
            System.Text.Encoding.UTF8.GetBytes(s) ' => 50
            System.Text.Encoding.UTF8.GetBytes("#") ' => 35
            Dim result as String = "(" + s + ") ###"
            result = result.replace("#","XX")
            return result
        end function
    
    function checkcode2(s as String) as String
        Dim result as String = "(" + s + ")"
        return result
    end function
    </script>
    <%
        body = "XXXXXXXXX Page #/#/# XXXXXXXXX"
        Dim reg As New Regex("(#)")
    
        REM Pattern 1
        body = reg.replace(body,checkcode("$1")) 
    
        REM Pattern 2
        body = body.Replace("#","!") 
    %>
    
[/code]

のようなコードを書き、(Pattern 1のとき)

[code]

    XXXXXXXXX Page (XX) XXXXXX/(XX) XXXXXX/(XX) XXXXXX XXXXXXXXX
    
[/code]

という結果を期待しました。しかし、これで得られる結果は

[code]

    XXXXXXXXX Page (#) XXXXXX/(#) XXXXXX/(#) XXXXXX XXXXXXXXX
    
[/code]

でした。そこで、checkcode2 に投げてみると、 (#) が帰ってきます。  
が、この時 s に入っている # を GetBytes で取得し、 # 部分の文字コードを見てみると 50 になっているのです。&#50;は 2
なので、期待値でもありません。

そこで、(Pattern 2)で置換してみると、こちらは問題なく置換ができました。  
この時の文字コードは 35 で、HTML上で &#35; と打てば # が出てきますので想定どおりです。

何故 Regex.Replace を通すと、 # が 35から50に変わってしまうのでしょうか。  
お昼から悩み続けて解決できず・・・よろしくお願いします。

\---------------------------------19:00 追記

例えば、 s を強制的に # の文字列に置き換えてみました。

[code]

    <!doctype html>
    <html>
    <head>
    <script runat="server" language="vb">
        function checkcode(s as String) as String
            Dim result as String = "(" + s + ") ###"
            result = result.replace("#","XX")
            return result
        end function
    
    
        function forcesharp(s as String) as String
            Dim result as String = "(#) ###"
            result = result.replace("#","XX")
            return result
        end function
    </script>
    </head>
    <body>
    
    <%
        Dim body as String = "XXXXXXXXX Page #/#/# XXXXXXXXX"
        Dim reg As New Regex("(#)")
    %>
        <%= reg.replace(body,checkcode("$1")) %>
    <hr />
        <%= reg.replace(body,forcesharp("$1")) %>
    
    </body>
    </html>
    
    
    結果
    XXXXXXXXX Page (#) XXXXXX/(#) XXXXXX/(#) XXXXXX XXXXXXXXX
    ------------------水平線--------------------
    XXXXXXXXX Page (XX) XXXXXX/(XX) XXXXXX/(XX) XXXXXX XXXXXXXXX
    
[/code]

forcesharp も checkcode も s には # が入っているはずですが、 s をそのまま使っているcheckcodeは #
なのに置換されていません。  
一方 forcesharp は s を使わず # の文字を改めて手入力していることも有り、正しく置換されています。  
バイトコードに置き換えると、手入力したシャープは 35、パターンマッチで取り出したシャープは 50 となっていますが、そもそも手入力した #
を検出しているはずなので # は 35のはず。  
\--------------------------------問題点が判明しました。  
Perl頭で合ったことが原因のようでした。  
Perlでは $body=~s/(#)/checkcode($1)/egsi; のように、$1 には #
が入ってくるので、思い込みからこのトラブルが発生していました。  
reg.replace(body,checkcode("$1"))は #ではなく $1が送られている、が原因でした。  
\--------------------------------解決策  
現在以下のページを参考に、想定していた動作のする手段で続行しています。  
<https://msdn.microsoft.com/ja-
jp/library/system.text.regularexpressions.matchevaluator(v=vs.110).aspx>


CC BY-SA 3.0
2016-10-20T09:17:30.707
29738
2016-10-20T10:56:45.593
2016-10-20T10:56:45.593
18423
18423
question
vb.net
VB.NETのRegex.Replace後方参照にて、"#" の文字コードが 50(本来35) になってしまう


パターン1の`checkcode`の内部の計算は`"($1) ###".Replace("#", "XX")`に纏められますが、この
**`String.Replace`では正規表現を考慮しません**ので、結果は`"($1) XXXXXX"`となり、結局

[code]

    Dim reg As New Regex("(#)")
    reg.Replace("XXXXXXXXX Page #/#/# XXXXXXXXX", "($1) XXXXXX")
    
[/code]

が評価されます。`reg`は`body`中の三か所の#にマッチし、それぞれの最初のグループには`reg`で括弧が指定されている#がキャプチャされます。これが置換文字列中の$1を置換するため各置換結果は`"(#)
XXXXXX"`となり、文字列全体では

> XXXXXXXXX Page (#) XXXXXX/(#) XXXXXX/(#) XXXXXX XXXXXXXXX

となります。

おそらく置換文字列側に「番号」を埋め込んでから`reg.Replace`を呼び出す必要があるのでは。

# 追記

コメントで誤解のポイントが分かったので追記しますが、.NETの関数は引数に指定した式の計算結果のみを受け取ります。ソースコード自体に文字列置換を行ってから評価するといったことは起こりません。

比較的元の意図に近い解決策としてはデリゲートを使うものがあります。  
`Regex.Replace`には第二引数に`MatchEvaluator`を受け取るものがあり、このデリゲートの実行結果が1個のマッチの置換結果となります。このデリゲート内で`Match.Result`メソッドを使用すれば置換文字列の展開が出来て、

[code]

    reg.Replace(body, Function(m) checkcode(m.Result("$1")))
    
[/code]

のように`"$1"`を評価してから`checkcode`に渡すことが出来ます。


CC BY-SA 3.0
2016-10-20T09:37:00.120
29740
2016-10-20T10:35:49.270
2020-06-17T08:14:45.997
-1
5750
29738
answer