replaceというのはXPath 2.0から使用できるようになった実に強力な関数です.
7.6.3 fn:replace
何が強力かというとそれは正規表現が使えてしまうからです.関数の定義は
fn:replace( $input as xs:string?,
$pattern as xs:string,
$replacement as xs:string) as xs:string
というものですけれど、$patternに正規表現を書けます.その中に()つきで抽出要素を指定すれば$replacementでそれを抜き出すことができてしまいます.例えば、画像の大きさがXXmm×YYmmというような表記になって$size変数に格納されていたとします.
<xsl:variable name="size" as="xs:string" select="150mm×90mm"/>
そうすると、この150と90という数値はsubstring-before(after)を使ってあたふたしなくとも
<xsl:variable name="mmSpec" as="xs:string" select="replace($size,'([0-9]+mm)×([0-9]+mm)','$1 $2')"/>
<xsl:variable name="mmSeq" as="xs:string+" select="tokenize($mmSpec,' ')"/>
で一発で取得できてしまいます.あとはXSL-FOに落とすなら
<fo:external-graphic src="~" content-width="{$mmSeq[1]}" content-height="{$mmSeq[2]}"/>
とするだけです.
でも時としてこの正規表現はヤバイときがあります.それは私たちが普段使う文字列置換は必ずしも正規表現を必要としない場合もあるからです.いえ、もっと言えばほとんどの場合、正規表現を使用しないと言った方が良いのかもしれません.
例えば次の例は、ファイルが見つからないというメッセージを組み立てようとしています.
replace('[Error 99] ファイルが見つかりません.パス=%path','%path','C:\Users\Toshi\Sample\diagram.png')
でもこれは次のエラーメッセージで失敗します.
FORX0004: Invalid replacement string in replace(): \ character must be followed by \ or $
理由は簡単です.\(バックスラッシュ)は正規表現で使用されるエスケープ文字だからです.ではどうしたら良いのでしょう?最初私は、"\"を"/"に変えて置換していました.ちょっと姑息ですね.もっと良い方法があります.それは以下のライブラリで公開されている関数を使用することです.
FunctX XQuery Functions
たとえばこのように$replacementに\が入っている場合は置換する前に\を\\にエスケープしてやります.このための関数は次のようになります.
<!--
function: escapeForRegx
param: prmSrcString
return: Escaped xs:string
note: Original code by Priscilla Walmsley.
-->
<xsl:function name="ahf:escapeForRegxDst" as="xs:string">
<xsl:param name="prmSrcString" as="xs:string"/>
<xsl:sequence select="replace($prmSrcString,'(\\|\$)','\\$1')"/>
</xsl:function>
<xsl:function name="ahf:escapeForRegxSrc" as="xs:string">
<xsl:param name="prmSrcString" as="xs:string"/>
<xsl:sequence select="replace($prmSrcString,'(\.|\[|\]|\\|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')"/>
</xsl:function>
<!--
function: Safe replace function
param: prmStr,prmSrc,prmDst
note: Result string
-->
<xsl:function name="ahf:safeReplace" as="xs:string">
<xsl:param name="prmStr" as="xs:string"/>
<xsl:param name="prmSrc" as="xs:string"/>
<xsl:param name="prmDst" as="xs:string"/>
<xsl:sequence select="replace($prmStr,ahf:escapeForRegxSrc($prmSrc),ahf:escapeForRegxDst($prmDst))"/>
</xsl:function>
あとはこれをreplaceの代わりにahf:safeReplaceを適用してやれば済みます.
[Error 99] ファイルが見つかりません.パス=C:\Users\Toshi\Sample\diagram.png
この関数の作者のPriscilla Walmsleyさんは、Definitive XML Schemaの著者でもあります.
FunctXのサイトには便利な関数がいろいろあります.ご覧いただければきっと役に立つでしょう.