Tcl/Tkプログラミングで役立つ`regsub`活用例:実践的な文字列操作テクニック
regsub
の基本的な構文
regsub ?スイッチ? exp string subSpec varName
各引数の意味は以下の通りです。
varName
: 置換結果が格納される変数名です。regsub
コマンド自体は置換された文字列を返さず、置換された回数(置換が行われなかった場合は0)を返します。subSpec
: マッチしたパターンを何に置換するかを指定する文字列です。この文字列内では、正規表現の後方参照(例:&
や\1
,\2
など)を使用できます。&
または\0
: マッチした文字列全体\1
: 最初の括弧で囲まれた部分(サブマッチ)\2
: 2番目の括弧で囲まれた部分- ...
string
: 検索と置換の対象となる元の文字列です。exp
: 検索する正規表現(Regular Expression)です。TclはPOSIX拡張正規表現をサポートしています。?スイッチ?
: オプションのスイッチです。よく使われるものとしては、-all
(マッチしたすべてのパターンを置換)、-nocase
(大文字・小文字を区別しないマッチング)などがあります。
最初のマッチだけを置換する
set text "Hello world, hello Tcl!"
regsub "hello" $text "Hi" result
puts $result
# 出力: Hi world, hello Tcl!
この例では、"hello"
というパターンが最初にマッチした部分だけが"Hi"
に置換され、その結果がresult
変数に格納されます。
すべてのマッチを置換する (-allスイッチ)
set text "Hello world, hello Tcl!"
regsub -all "hello" $text "Hi" result
puts $result
# 出力: Hi world, Hi Tcl!
-all
スイッチを使うと、マッチしたすべての"hello"
が"Hi"
に置換されます。
大文字・小文字を区別しない置換 (-nocaseスイッチ)
set text "Hello world, hello Tcl!"
regsub -all -nocase "hello" $text "Greetings" result
puts $result
# 出力: Greetings world, Greetings Tcl!
-nocase
スイッチを追加すると、大文字・小文字を区別せずにパターンマッチングが行われます。この例では、"Hello"
と"hello"
の両方が対象となります。
後方参照の使用
set email "[email protected]"
# ドメイン名だけを抽出して大文字にする例
# (\w+)は単語文字の連続にマッチし、最初の括弧で囲まれた部分が\1になる
# (\.\w+)はドットとそれに続く単語文字の連続にマッチし、2番目の括弧で囲まれた部分が\2になる
regsub {^(\w+)@(\w+)(\.\w+)$} $email {\1@\U\2\E\3} result
puts $result
# 出力: [email protected]
この例では、メールアドレスからユーザー名、ドメイン名、トップレベルドメインを抽出し、ドメイン名部分だけを大文字に変換しています。
\U...\E
:subSpec
内で、\U
から\E
までの文字を大文字に変換します。(\.\w+)$
: ドットとそれに続く単語文字の連続を行末までグループ3としてキャプチャ(\w+)
:@
の後の単語文字の連続をグループ2としてキャプチャ(これがドメイン名)@
:@
記号^(\w+)
: 行の先頭から始まる単語文字の連続をグループ1としてキャプチャ
文字の削除
特定の文字やパターンを削除したい場合は、subSpec
を空文字列に指定します。
set phonenumber "123-456-7890"
regsub -all "-" $phonenumber "" result
puts $result
# 出力: 1234567890
正規表現の誤り (Regular Expression Errors)
これは最も一般的な問題です。
-
アンカー(^, $)の誤用
^
は行頭、$
は行末にマッチします。これらを使いこなすことで、マッチ範囲を正確に制御できますが、誤って使うと全くマッチしなくなったり、予期せぬ結果になったりします。 -
貪欲マッチと非貪欲マッチの誤解
*
,+
,?
などの量指定子はデフォルトで「貪欲(greedy)」にマッチします。つまり、可能な限り長い文字列にマッチしようとします。非貪欲(non-greedy)にマッチさせたい場合は、量指定子の後に?
を付けます(例:*?
,+?
)。- 例
HTMLタグ<p>...</p>
の中から内容だけを抽出したい場合、<p>.*</p>
とすると、途中に複数の<p>...</p>
があっても、最初の<p>
から最後の</p>
まで全てマッチしてしまうことがあります。正しくは<p>.*?</p>
のように記述します。 - トラブルシューティング
期待するよりも広い範囲がマッチしてしまう場合は、非貪欲マッチの適用を検討してください。
- 例
-
正規表現の構文エラー
不完全なグループ化((
が閉じられていないなど)、無効な文字クラス、不適切な量指定子など。- 例
{X"[A-Z0-9]*
のように、閉じ括弧が欠けている場合。 - トラブルシューティング
Tclは正規表現の構文エラーに対して比較的親切なエラーメッセージを出すことが多いので、メッセージを注意深く読んでください。オンラインの正規表現テスター(Regex101など)で正規表現をテストしてみるのも非常に有効です。
- 例
-
特殊文字のエスケープ漏れ
正規表現には多くの特殊文字(.
,*
,+
,?
,[
,]
,(
,)
,{
,}
,^
,$
,\
,|
など)があります。これらを文字通りの意味でマッチさせたい場合は、\
(バックスラッシュ)でエスケープする必要があります。- 例
文字列[0]
をマッチさせたいのに{elem[0]}
と書くと、[0]
が「0の文字クラス」と解釈されてしまいます。正しくは{elem\\[0\\]}
のように書く必要があります。 - トラブルシューティング
正規表現が期待通りに動作しない場合、まず特殊文字のエスケープを確認してください。特に、[
や]
、(
や)
などの括弧はTclのリスト構造と正規表現のグループ化の両方で特殊な意味を持つため、混乱しやすいです。
- 例
Tclの評価ルールとクォーティング (Tcl Evaluation Rules and Quoting)
Tclはコマンドラインの評価において、二重引用符("
)、波括弧({}
)、バックスラッシュ(\
)、ドル記号($
)、角括弧([]
)など、様々な特殊文字を扱います。これがregsub
を使う際に混乱の元となることがあります。
-
varName引数の上書き
regsub
は、置換結果を最後の引数で指定された変数に格納します。置換が行われなかった場合でも、その変数の内容はクリアされるか、元の文字列がコピーされます。- 例
set my_string "original" regsub "pattern_not_found" $my_string "replacement" my_string puts $my_string ;# "original" のままではなく、置換が行われなかったため元の文字列がコピーされる(この場合も実質"original")
- 複数の
regsub
を連続して使う場合、前のregsub
の結果が意図せず上書きされることがあります。 - トラブルシューティング
各regsub
の結果を別の変数に格納するか、パイプライン処理([regsub ... [regsub ... $text]]
)を検討してください。
- 例
-
変数展開 ($) とバックスラッシュの二重処理
- 正規表現パターンや置換文字列に変数を組み込みたい場合、二重引用符
"
を使うと変数展開が行われます。しかし、"
の中ではバックスラッシュもTclによって一度処理されます。 - 例
set pattern "\\d+"
のように変数に正規表現を格納し、regsub $pattern $string "" result
とした場合、pattern
変数に格納されるのは\d+
ではなく、d+
になってしまいます。正規表現の\
を文字通り扱わせるためには、set pattern "\\\\d+"
のようにバックスラッシュを二重にエスケープするか、波括弧{}
を使ってset pattern {\d+}
とする必要があります。 - トラブルシューティング
正規表現や置換文字列を波括弧{}
で囲むのが最も安全で推奨される方法です。波括弧内では、変数展開やバックスラッシュエスケープなどのTclの特殊な解釈が行われないため、正規表現をそのまま記述できます。- ただし、置換文字列で後方参照(
\1
,\2
など)を使いたい場合は、波括弧内でもバックスラッシュが機能します。これはTclのバックスラッシュエスケープではなく、regsub
コマンド自身が置換文字列内で\
を特殊文字として解釈するためです。 - 変数を正規表現や置換文字列に含める必要がある場合は、文字列結合(
append
,format
など)や、正規表現の引数だけを二重引用符で囲むなどの工夫が必要です。set my_var "world" regsub "hello ($my_var)" $text "Hi \1" result ;# これは動く regsub {hello ($my_var)} $text {Hi \1} result ;# これは動かない ($my_varがそのまま文字列として扱われる) regsub "hello ($my_var)" $text {Hi \1} result ;# 正規表現は"で囲み変数展開、置換は{}で囲む
- ただし、置換文字列で後方参照(
- 正規表現パターンや置換文字列に変数を組み込みたい場合、二重引用符
マッチしない/期待通りに置換されない (No Match / Unexpected Replacement)
-
regsubの戻り値の誤解
regsub
コマンド自体は、置換された回数(マッチがなかった場合は0)を返します。置換された文字列はvarName
引数で指定した変数に格納されます。- 例
set old_string "test" set count [regsub "t" $old_string "x" new_string] puts "Count: $count" ;# Count: 1 puts "Result: $new_string" ;# Result: xest
- これを知らずに
set result [regsub ...]
として、result
変数に置換された文字列が入っていると期待してしまうことがあります。
- 例
-
置換文字列の後方参照の誤用
\1
や\2
などの後方参照が、正規表現内のグループ化(括弧()
)と一致していない。- 置換文字列内で
\
が文字通りの意味で使われてしまい、後方参照として解釈されない(通常はsubSpec
内で\
はregsub
自身によって解釈されるため、これは稀なケースですが、二重引用符と組み合わせて複雑なエスケープが必要な場合に発生し得ます)。 - トラブルシューティング
正規表現の括弧の数と、置換文字列の後方参照の番号が合っているか確認してください。
-
パターンが実際にはマッチしない
- 入力文字列と正規表現の間に予期せぬ空白文字や改行文字が含まれている。
- 大文字・小文字の区別(
regsub -nocase
の使用忘れ)。 - 正規表現が意図したパターンを捕捉できていない。
- トラブルシューティング
regexp
コマンドを使って、まず正規表現が正しくマッチするかどうかを確認してください。regexp
はマッチの有無を返すだけでなく、-indices
オプションを使えばマッチした部分の開始/終了インデックスも得られます。これにより、正規表現のデバッグがしやすくなります。
set text "Hello World" if {[regexp {hello} $text]} { puts "Matched!" } else { puts "No match." } # マッチしなかった場合 if {[regexp -nocase {hello} $text]} { puts "Matched (case-insensitive)!" }
- 複雑すぎる正規表現
非常に複雑な正規表現や、バックトラックが多発するような非効率な正規表現(例:(a*)*
)は、処理に時間がかかったり、最悪の場合フリーズしたりすることがあります(ReDoS攻撃の可能性)。- トラブルシューティング
シンプルな正規表現で問題を解決できないか検討し、必要に応じてstring
コマンドの他のサブコマンド(string map
,string replace
,string trim
など)との組み合わせを検討してください。
- トラブルシューティング
- 正規表現の基本を理解する
貪欲/非貪欲マッチ、文字クラス、量指定子、アンカー、後方参照などの概念をしっかりと理解することが、効率的で正確なregsub
の使用につながります。 - varName引数を理解する
regsub
の戻り値は置換回数であり、置換後の文字列は指定した変数に格納されます。 - -all, -nocaseなどのスイッチを正しく使う
意図しないマッチや置換を避けるために、これらのスイッチの有無が重要です。 - まずregexpでテストする
regsub
で置換を行う前に、regexp
コマンドを使って正規表現が期待通りにマッチするかを確認することは、デバッグの時間を大幅に短縮します。 - 正規表現と置換文字列は基本的に波括弧 {} で囲む
これにより、Tclの余計な解釈を避け、正規表現や置換文字列をそのまま記述できます。変数展開が必要な場合は、format
コマンドなどで事前に文字列を組み立てるか、正規表現部分だけを二重引用符で囲むなどの工夫をします。
基本的な文字列の置換
最も基本的な使い方です。特定のパターンを別の文字列に置換します。
# 例1: 最初のマッチだけを置換
set original_text "The quick brown fox jumps over the lazy fox."
regsub "fox" $original_text "dog" result_single
puts "最初の置換結果: $result_single"
# 出力: 最初の置換結果: The quick brown dog jumps over the lazy fox.
# 例2: すべてのマッチを置換 (-all スイッチ)
set original_text "Hello World, hello Tcl!"
regsub -all "hello" $original_text "Hi" result_all
puts "すべての置換結果: $result_all"
# 出力: すべての置換結果: Hi World, Hi Tcl!
# 例3: 大文字・小文字を区別しない置換 (-nocase スイッチ)
set original_text "Apple, banana, APPLe, orange."
regsub -all -nocase "apple" $original_text "Grape" result_nocase
puts "大文字・小文字無視の置換結果: $result_nocase"
# 出力: 大文字・小文字無視の置換結果: Grape, banana, Grape, orange.
特定の文字の削除 (空白、ハイフンなど)
subSpec
を空文字列にすることで、マッチしたパターンを削除できます。
# 例1: 数字以外の文字を削除して数字だけを抽出
set phone_number "Call me at (123) 456-7890 Ext. 123."
regsub -all {\D} $phone_number "" digits_only
puts "数字のみ: $digits_only"
# 出力: 数字のみ: 1234567890123
# 例2: 複数の空白文字を単一のスペースに置換
set messy_string "This is a messy string."
regsub -all { +} $messy_string " " cleaned_string
puts "空白整理済み: $cleaned_string"
# 出力: 空白整理済み: This is a messy string.
{ +}
: 1つ以上の空白文字にマッチ(波括弧で囲むことで、Tclの評価による影響を受けないようにする)\D
: 数字以外の文字にマッチ
後方参照 (\1, \2 など) を使った置換
正規表現の括弧でキャプチャした部分 ((
と )
) を置換文字列内で参照します。
# 例1: 日付形式の変換 (YYYY/MM/DD -> MM-DD-YYYY)
set date_ymd "2023/10/26"
# 正規表現: (年)/(月)/(日) をそれぞれグループ1, 2, 3としてキャプチャ
regsub {^(\d{4})/(\d{2})/(\d{2})$} $date_ymd {\2-\3-\1} date_mdy
puts "日付変換結果: $date_mdy"
# 出力: 日付変換結果: 10-26-2023
# 例2: 名前の順序を入れ替える (Last, First -> First Last)
set full_name "Doe, John"
# 正規表現: (姓), (名) をそれぞれグループ1, 2としてキャプチャ
regsub {^([^,]+), (.+)$} $full_name {\2 \1} reversed_name
puts "名前の順序変更: $reversed_name"
# 出力: 名前の順序変更: John Doe
# 例3: HTMLタグの属性値を変更
set html_tag {<img src="old.jpg" alt="A photo">}
# 正規表現: src="任意の文字(非貪欲)" をキャプチャし、その中のファイル名をグループ1としてキャプチャ
regsub {src="([^"]*?)"} $html_tag {src="new_image.png"} updated_tag
puts "HTMLタグ更新: $updated_tag"
# 出力: HTMLタグ更新: <img src="new_image.png" alt="A photo">
[^"]*?
: 二重引用符以外の文字が0回以上続く(非貪欲).+
: 任意の文字が1回以上続く[^,]+
: カンマ以外の文字が1回以上続く\d{4}
: 4桁の数字
文字列の一部を大文字/小文字に変換
置換文字列内で \U...\E
(大文字化) や \L...\E
(小文字化) を使用します。
# 例1: ドメイン名を大文字に変換
set url "http://www.example.com/index.html"
# 正規表現: www.サブドメイン.トップレベルドメイン をグループ1としてキャプチャ
regsub {^(https?://www\.)([^/]+)(/.*)$} $url {\1\U\2\E\3} capitalized_domain_url
puts "ドメイン名大文字化: $capitalized_domain_url"
# 出力: ドメイン名大文字化: http://www.EXAMPLE.COM/index.html
# 例2: 最初の単語だけを小文字に変換
set sentence "HELLO World, How Are You?"
regsub {^(\S+)} $sentence {\L\1\E} lowercased_first_word
puts "最初の単語を小文字化: $lowercased_first_word"
# 出力: 最初の単語を小文字化: hello World, How Are You?
\L...\E
: 小文字に変換する区間\U...\E
: 大文字に変換する区間\S+
: 空白以外の文字が1回以上続く
regsubの戻り値と変数への格納
regsub
コマンド自体は置換が行われた回数を返し、置換後の文字列は最後の引数で指定された変数に格納されます。
set my_string "apple banana apple orange"
# "apple"を"grape"に置換し、置換回数を取得
set replace_count [regsub -all "apple" $my_string "grape" final_string]
puts "元の文字列: $my_string"
puts "置換回数: $replace_count"
puts "最終文字列: $final_string"
# 出力:
# 元の文字列: apple banana apple orange
# 置換回数: 2
# 最終文字列: grape banana grape orange
# マッチしなかった場合
set no_match_count [regsub "xyz" $my_string "abc" result_no_match]
puts "マッチなしの置換回数: $no_match_count"
puts "マッチなしの結果文字列: $result_no_match"
# 出力:
# マッチなしの置換回数: 0
# マッチなしの結果文字列: apple banana apple orange
正規表現パターンに変数を埋め込む場合は、二重引用符"
を使うか、format
コマンドなどで事前にパターン文字列を構築します。
set search_word "World"
set original_text "Hello World, Hello Tcl!"
# 二重引用符を使って変数を展開
regsub $search_word $original_text "Universe" result1
puts "変数を正規表現に: $result1"
# 出力: 変数を正規表現に: Hello Universe, Hello Tcl!
# より安全な方法: formatでパターンを構築し、波括弧で渡す
set pattern_var [format {Hello (%s)} $search_word]
regsub $pattern_var $original_text {Hi \1} result2
puts "formatと変数を正規表現に: $result2"
# 出力: formatと変数を正規表現に: Hi World, Hello Tcl!
# Note: この場合、パターン内の$search_wordは`format`で展開されてから`regsub`に渡されるため、`regsub`が受け取る正規表現は`Hello (World)`となります。
# 置換文字列に変数を埋め込む場合も同様
set replacement_word "Planet"
regsub $search_word $original_text "$replacement_word" result3
puts "置換文字列に変数を: $result3"
# 出力: 置換文字列に変数を: Hello Planet, Hello Tcl!
string map コマンド
複数の固定文字列を一度に置換する場合に最適です。正規表現は使用せず、文字通りのマッチングを行います。
構文
string map {old1 new1 old2 new2 ...} string
特徴
- 置換順序が重要です。リストの前の方にあるルールが先に適用されます。
- 大文字・小文字を区別します(
-nocase
オプションはありません)。 - 複数の置換ルールを一度に指定できます。
- 正規表現を使用しないため、シンプルで高速です。
regsubの代替となる例
特定の単語を別の単語に、または複数の文字を別の文字に置換する場合。
set text "apple banana orange apple"
# regsub を使った場合
regsub -all "apple" $text "grape" result_regsub
puts "regsub: $result_regsub"
# 出力: regsub: grape banana orange grape
# string map を使った場合
set replace_map {
apple grape
banana cherry
}
set result_string_map [string map $replace_map $text]
puts "string map: $result_string_map"
# 出力: string map: grape cherry orange grape
# 複数の文字を置換する場合 (例: 半角カタカナを全角に変換する一例)
set han_kana "テスト"
set zen_kana_map {
"テ" "テ"
"ス" "ス"
"ト" "ト"
}
set result_kana [string map $zen_kana_map $han_kana]
puts "半角->全角: $result_kana"
# 出力: 半角->全角: テスト
string replace コマンド
文字列の特定の位置から特定の位置までを別の文字列で置き換える場合に利用します。インデックスベースの置換です。
構文
string replace string first last ?newstring?
特徴
- 特定の範囲の文字列を削除したり、挿入したりするのに便利です。
- 文字の位置(インデックス)を指定して置換するため、パターンマッチングには不向きです。
- 正規表現は使用しません。
regsubの代替となる例
文字列の先頭や末尾、または既知の固定位置の文字列を置換する場合。
set text "Hello World!"
# regsub を使った場合 (先頭の"Hello"を置換)
regsub "^Hello" $text "Hi" result_regsub
puts "regsub: $result_regsub"
# 出力: regsub: Hi World!
# string replace を使った場合 (先頭から5文字目までを置換)
set result_string_replace [string replace $text 0 4 "Hi"]
puts "string replace: $result_string_replace"
# 出力: string replace: Hi World!
# 特定の範囲を削除する場合 (newstringを省略)
set result_delete [string replace $text 5 5 ""] ;# " " (スペース) を削除
puts "文字削除: $result_delete"
# 出力: 文字削除: HelloWorld!
split と join コマンドの組み合わせ
特定の区切り文字で文字列を分割し、リスト操作を行った後、再度結合することで置換と似た効果を得ることができます。
構文
split string ?splitChars?
join list ?joinString?
特徴
- 複雑なルールでの置換や、リストの要素ごとに異なる処理を行う場合に柔軟性があります。
- 正規表現は使用できません(区切り文字は固定文字列)。
regsubの代替となる例
特定の区切り文字で区切られた部分を置換したり、各部分に処理を加えたりする場合。
set comma_separated "apple,banana,orange"
# regsub を使った場合 (全てのコンマをハイフンに置換)
regsub -all "," $comma_separated "-" result_regsub
puts "regsub: $result_regsub"
# 出力: regsub: apple-banana-orange
# split と join を使った場合
set list_elements [split $comma_separated ","]
set result_split_join [join $list_elements "-"]
puts "split/join: $result_split_join"
# 出力: split/join: apple-banana-orange
# 各要素に処理を加えてから結合する場合
set elements_with_prefix [lmap item $list_elements {
string toupper $item ;# 各要素を大文字化
}]
set result_processed [join $elements_with_prefix "_"]
puts "処理後結合: $result_processed"
# 出力: 処理後結合: APPLE_BANANA_ORANGE
regexp
でパターンマッチングを行い、マッチした情報(開始位置、長さ、サブマッチなど)を使ってstring range
やstring replace
などのコマンドで文字列を構築し直す方法です。これはregsub
が持つ機能を「手動で」実装するようなイメージです。
構文
regexp ?スイッチ? exp string ?matchVar? ?subMatchVar ...?
特徴
- コードは
regsub
を直接使うよりも複雑になります。 - 複数のマッチングに対して、それぞれ異なる置換処理を行いたい場合に有用です。
- 置換ロジックを非常に細かく制御できます。
regsub
と同等の正規表現の柔軟性があります。
regsubの代替となる例
マッチした部分ごとに異なるロジックで置換したい場合。
set text "Number: 123, Text: ABC, Number: 456"
# regsub で全ての数字を"[数字]"の形式に置換する場合
regsub -all {(\d+)} $text {[\1]} result_regsub
puts "regsub: $result_regsub"
# 出力: regsub: Number: [123], Text: ABC, Number: [456]
# regexp と string replace を使って手動で置換する場合
# (これは少し冗長ですが、カスタムロジックの可能性を示すものです)
set result_manual $text
set offset 0
# -all を使って全てのマッチをループ
# matchInfo は {全体マッチの開始位置 全体マッチの長さ サブマッチ1の開始位置 サブマッチ1の長さ ...}
while {[regexp -indices -all {(\d+)} $result_manual matchInfo]} {
# マッチした各部分について処理
foreach {full_start full_end sub1_start sub1_end} $matchInfo {
# offset を加算して正しいグローバルインデックスに調整
set actual_full_start [expr {$full_start + $offset}]
set actual_full_end [expr {$full_end + $offset}]
set actual_sub1_start [expr {$sub1_start + $offset}]
set actual_sub1_end [expr {$sub1_end + $offset}]
# サブマッチした数字を取得
set number [string range $result_manual $actual_sub1_start $actual_sub1_end]
set replacement_string "\[$number\]"
# string replace で置換
set result_manual [string replace $result_manual $actual_full_start $actual_full_end $replacement_string]
# 置換によって文字列長が変わった分、offsetを調整
set offset [expr {$offset + [string length $replacement_string] - ($actual_full_end - $actual_full_start + 1)}]
}
}
puts "regexp+string replace: $result_manual"
# 出力: regexp+string replace: Number: [123], Text: ABC, Number: [456]
この手動の例は、regsub
がいかに便利であるかを逆説的に示していますが、マッチした数字が偶数なら大文字にする、といったような複雑な条件分岐を伴う置換が必要な場合には、regexp
と手動での文字列操作が選択肢となります。
代替方法 | 特徴 | 適切なシナリオ |
---|---|---|
string map | 正規表現なし。複数の固定文字列を高速に置換。大文字・小文字を区別。 | 複数の既知の固定文字列を一括置換する、または特定の文字を他の文字に変換する(例: 全角半角変換の一部)。 |
string replace | 正規表現なし。インデックスベースで文字列の一部を置換/挿入/削除。 | 文字列の先頭、末尾、または既知の固定位置の文字を操作する。 |
split /join | 正規表現なし。固定区切り文字で分割し、リスト処理後に再結合。 | 固定の区切り文字で区切られた要素を操作する。各要素に何らかの処理を加えてから結合し直す。 |
regexp と手動処理 | 正規表現使用。マッチした情報を取得し、手動で文字列を構築。最も柔軟だが、コードが複雑になる。 | regsub では対応できないような、マッチした内容に基づいて動的に置換文字列を生成する(例: マッチした値の計算結果を置換文字列とする)。 |