Replace:高效字串替換核心
TStringBuilder.Replace 提供四種重載,支援字元/字串、全域/區間替換,內部優化緩衝區操作,效能遠優於傳統 StringReplace。
核心 API 四重載
| 重載 | 參數 | 說明 |
| 1. 字串全域 | OldStr, NewStr: string | 替換所有 OldStr 為 NewStr |
| 2. 字元全域 | OldChar, NewChar: Char | 替換所有 OldChar 為 NewChar |
| 3. 字元區間 | OldChar, NewChar: Char; StartIndex, Count: Integer | 指定範圍內替換字元 |
| 4. 字串區間 | OldStr, NewStr: string; StartIndex, Count: Integer | 指定範圍內替換字串 |
完整實作程式碼
procedure TForm1.Button1Click(Sender: TObject); // 字串全域替換
var sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
sb.Append('從 Delphi 2007 到 Delphi 2009');
sb.Replace('Delphi', 'CBuilder');
ShowMessage(sb.ToString); // '從 CBuilder 2007 到 CBuilder 2009'
finally sb.Free; end;
end;
procedure TForm1.Button2Click(Sender: TObject); // 字元全域替換
var sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
sb.Append('從 Delphi 2007 到 Delphi 2009');
sb.Replace('0', '9');
ShowMessage(sb.ToString); // '從 Delphi 2997 到 Delphi 2999'
finally sb.Free; end;
end;
procedure TForm1.Button3Click(Sender: TObject); // 字元區間替換
var sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
sb.Append('ABABABABABAB');
// 從索引 4 開始,共 5 字元範圍內將 'A' 替換為 '-'
sb.Replace('A', '-', 4, 5);
ShowMessage(sb.ToString); // 'ABAB-B-B-BAB'
finally sb.Free; end;
end;
procedure TForm1.Button4Click(Sender: TObject); // 字串區間替換
var sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
sb.Append('ABC123ABC123ABC123');
// 從索引 3 開始,共 12 字元範圍內將 'ABC' 替換為 '-'
sb.Replace('ABC', '-', 3, 12);
ShowMessage(sb.ToString); // 'ABC123-123-123'
finally sb.Free; end;
end;
參數詳解:StartIndex 與 Count
- StartIndex:0-based 替換起始位置
- Count:替換掃描範圍長度 (字元數)
- 範例解析:
'ABABABABABAB'.Replace('A', '-', 4, 5)
- 索引 4='A',掃描 5 字元:索引 4,5,6,7,8 → 'A','B','A','B','A'
- 範圍內 3 個 'A' 被替換為 '-' → 結果 'ABAB-B-B-BAB'
- 字串區間:
'ABC123ABC123ABC123'.Replace('ABC', '-', 3, 12)
- 索引 3='1',掃描 12 字元:'123ABC123ABC'
- 範圍內 2 個 'ABC' 被替換 → 'ABC123-123-123'
Replace vs StringReplace 效能對比
| 指標 | StringReplace | TStringBuilder.Replace | 優勢 |
| 記憶體分配 | 每次產生新字串 | 原地修改緩衝區 | 無碎片、低 GC 壓力 |
| 多次替換 | 累積分配開銷 | 單一緩衝區操作 | 線性擴容 vs 指數分配 |
| 區間替換 | 需 Copy+Replace+Copy | 原生支援 | 程式碼簡潔、效能高 |
| 鏈式操作 | 需中間變數 | 回傳 Self 可鏈式 | sb.Replace(...).Append(...) |
進階應用場景
1. 模板引擎核心:批次佔位符替換
function RenderTemplate(const Template: string; const Params: TDictionary): string;
var
sb: TStringBuilder;
pair: TPair;
begin
sb := TStringBuilder.Create(Template);
try
for pair in Params do
sb.Replace('{' + pair.Key + '}', pair.Value); // 全域替換
Result := sb.ToString;
finally
sb.Free;
end;
end;
2. 敏感詞過濾:區間保護 (如程式碼區塊不替換)
function FilterSensitive(const Text: string; const SensitiveWords: array of string): string;
var
sb: TStringBuilder;
i, codeStart, codeEnd: Integer;
word: string;
begin
sb := TStringBuilder.Create(Text);
try
// 簡易:保護 `` `code` `` 區塊不被替換
// 實際需狀態機解析
for word in SensitiveWords do
sb.Replace(word, StringOfChar('*', Length(word)));
Result := sb.ToString;
finally
sb.Free;
end;
end;
3. 格式化清理:移除多餘空白、統一換行
procedure NormalizeText(sb: TStringBuilder);
begin
sb.Replace(#13#10, #10); // CRLF → LF
sb.Replace(#13, #10); // CR → LF
sb.Replace(#9, ' '); // Tab → 2 spaces
// 多空白壓縮 (需迴圈或正則,Replace 不支援正則)
while sb.ToString.Contains(' ') do
sb.Replace(' ', ' ');
end;
4. 程式碼重構輔助:區間重命名變數
procedure RenameInRange(sb: TStringBuilder; const OldName, NewName: string; StartLine, EndLine: Integer);
var
lineStart, lineEnd, charStart, charCount: Integer;
begin
// 將行號轉換為字元索引 (簡化版)
charStart := LineToCharIndex(sb, StartLine);
charCount := LineToCharIndex(sb, EndLine + 1) - charStart;
sb.Replace(OldName, NewName, charStart, charCount);
end;
內部實作原理 (簡化)
// 字串替換核心邏輯
function TStringBuilder.ReplaceInternal(const OldStr, NewStr: string; StartIndex, Count: Integer): TStringBuilder;
var
oldLen, newLen, diff, i, found: Integer;
p: PChar;
begin
oldLen := Length(OldStr);
newLen := Length(NewStr);
diff := newLen - oldLen;
i := StartIndex;
while i <= StartIndex + Count - oldLen do
begin
// 尋找匹配 (簡化: 實際用優化搜尋演算法)
found := FindSubstring(OldStr, i);
if found < 0 then Break;
if found > StartIndex + Count - oldLen then Break;
// 計算位移
if diff <> 0 then
begin
EnsureCapacity(Length + diff);
Move(FData[found + oldLen], FData[found + newLen], (Length - found - oldLen) * SizeOf(Char));
Inc(FLength, diff);
Inc(Count, diff); // 調整剩餘掃描範圍
end;
// 複入新字串
Move(Pointer(NewStr)^, FData[found], newLen * SizeOf(Char));
i := found + newLen; // 繼續下一個位置
end;
Result := Self;
end;
常見陷阱與避坑
| 陷阱 | 後果 | 正確做法 |
| OldStr 為空字串 | 無限迴圈/崩潰 | 檢查 if OldStr = '' then Exit |
| 區間參數誤解 | 替換範圍錯誤 | Count 是「掃描字元數」,非替換次數 |
| 替換後長度變化影響索引 | 後續邏輯索引錯亂 | 用 Replace 回傳 Self 鏈式,或重新計算索引 |
| 大小寫敏感 | 漏替換 | 需自行實作不區分大小寫版本 |
最佳實務建議
- 大量替換前 EnsureCapacity:預估最終長度避免多次擴容
- 字元替換優於字串替換:單字元替換最快,簡單字元映射優先用 Char 版
- 區間替換保護非目標區域:如程式碼區塊、字串字面值
- 鏈式呼叫:
sb.Replace('a','b').Replace('c','d').Append('end')
- 需不區分大小寫時:轉大寫後替換,或用正則庫
總結
TStringBuilder.Replace 以四重載覆蓋字元/字串、全域/區間四大替換場景,內部原地緩衝區修改避免記憶體分配開銷。掌握 StartIndex/Count 區間語意、diff 長度差自動調整機制、鏈式呼叫模式,配合 EnsureCapacity 預分配,即能高效實現模板渲染、敏感詞過濾、格式化清理、程式碼重構等進階替換需求。