動態字串編輯核心:Insert 與 Remove
TStringBuilder 提供 Insert 與 Remove 方法,支援在緩衝區任意位置進行字串插入與刪除,避免傳統字串拼接的大量記憶體分配與複製開銷。
核心 API
| 方法 | 參數 | 說明 |
Insert | Index: Integer; Value: string/Char/其他型別 | 在 Index 位置插入值,後續字元後移 |
Remove | Index: Integer; Count: Integer | 從 Index 刪除 Count 個字元 |
完整實作程式碼
procedure TForm1.Button1Click(Sender: TObject);
var
sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
sb.Append('CodeGear 2007');
// 在 'CodeGear ' (長度 9) 後插入 'Delphi '
sb.Insert(Length('CodeGear '), 'Delphi ');
ShowMessage(sb.ToString); // 'CodeGear Delphi 2007'
// 刪除 ' 2007' (從 'CodeGear Delphi' 長度 14 開始,長度 5)
sb.Remove(Length('CodeGear Delphi'), Length(' 2007'));
ShowMessage(sb.ToString); // 'CodeGear Delphi'
// 在末尾插入 ' 2009'
sb.Insert(sb.Length, ' 2009');
ShowMessage(sb.ToString); // 'CodeGear Delphi 2009'
finally
sb.Free;
end;
end;
參數詳解與索引語意
Insert(Index, Value)
- Index:0-based 插入位置。
0 = 最前面,Length = 末尾 (等同 Append)
- Value:支援
string、Char、數值型別、物件等 (內部轉字串)
- 行為:原 Index 及之後字元右移,
Length 增加 Length(Value)
Remove(Index, Count)
- Index:0-based 刪除起始位置
- Count:刪除字元數
- 行為:Index+Count 之後字元左移填補,
Length 減少 Count
- 邊界:
Index + Count <= Length,否則拋出例外
進階應用場景
1. 模板字串填充:佔位符替換
function FillTemplate(const Template: string; const Values: array of string): string;
var
sb: TStringBuilder;
i, pos: Integer;
placeholder: string;
begin
sb := TStringBuilder.Create(Template);
try
for i := 0 to High(Values) do
begin
placeholder := '{' + IntToStr(i) + '}';
pos := sb.ToString.IndexOf(placeholder);
if pos >= 0 then
begin
sb.Remove(pos, Length(placeholder));
sb.Insert(pos, Values[i]);
end;
end;
Result := sb.ToString;
finally
sb.Free;
end;
end;
// 使用: FillTemplate('Hello {0}, welcome to {1}!', ['User', 'Delphi'])
// → 'Hello User, welcome to Delphi!'
2. 高效字串建構:預留位置後填充
procedure BuildReport(sb: TStringBuilder; const Sections: array of string);
const
SEP = ' | ';
var
i, pos: Integer;
begin
// 預建結構: [Section0] | [Section1] | ...
for i := 0 to High(Sections) do
begin
if i > 0 then sb.Append(SEP);
pos := sb.Length; // 記錄插入點
sb.Append('[Section' + IntToStr(i) + ']'); // 佔位符
// 稍後填充真實內容
sb.Remove(pos, Length('[Section' + IntToStr(i) + ']'));
sb.Insert(pos, Sections[i]);
end;
end;
3. 實作類 StringBuilder.ReplaceAll (全域替換)
procedure ReplaceAll(sb: TStringBuilder; const OldStr, NewStr: string);
var
pos: Integer;
oldLen: Integer;
begin
oldLen := Length(OldStr);
if oldLen = 0 then Exit;
pos := 0;
while True do
begin
pos := sb.ToString.IndexOf(OldStr, pos);
if pos < 0 then Break;
sb.Remove(pos, oldLen);
sb.Insert(pos, NewStr);
pos := pos + Length(NewStr); // 繼續搜尋替換後位置
end;
end;
4. 緩衝區縮減:移除前後空白
procedure TrimInPlace(sb: TStringBuilder);
var
start, finish: Integer;
begin
// 找第一個非空白
start := 0;
while (start < sb.Length) and (sb.Chars[start] <= ' ') do Inc(start);
// 找最後一個非空白
finish := sb.Length - 1;
while (finish >= 0) and (sb.Chars[finish] <= ' ') do Dec(finish);
if start > 0 then sb.Remove(0, start);
if finish < sb.Length - 1 then sb.Remove(finish + 1, sb.Length - finish - 1);
end;
Insert/Remove vs 字串操作效能對比
| 操作 | String (s := s + ...) | TStringBuilder Insert/Remove | 優勢 |
| 中間插入 | O(n) 分配新字串 + 複製 | O(n) 緩衝區移動 (無新分配) | 避免記憶體碎片 |
| 中間刪除 | O(n) 分配新字串 + 複製 | O(n) 緩衝區移動 | 同理 |
| 多次連續編輯 | 指數級分配 | 線性緩衝區擴容 | 巨大優勢 |
| 最終 ToString | 已是字串 | 單次分配輸出 | 可控 |
內部實作原理 (簡化)
// Insert 核心邏輯
procedure TStringBuilder.Insert(Index: Integer; const Value: string);
var valLen: Integer;
begin
valLen := Length(Value);
EnsureCapacity(Length + valLen); // 確保容量
// 移動現有資料騰出空間
Move(FData[Index], FData[Index + valLen], (Length - Index) * SizeOf(Char));
// 複入新值
Move(Pointer(Value)^, FData[Index], valLen * SizeOf(Char));
Inc(FLength, valLen);
end;
// Remove 核心邏輯
procedure TStringBuilder.Remove(Index, Count: Integer);
begin
// 移動後續資料覆蓋刪除區段
Move(FData[Index + Count], FData[Index], (Length - Index - Count) * SizeOf(Char));
Dec(FLength, Count);
end;
常見陷阱與避坑
| 陷阱 | 後果 | 正確做法 |
| Index 超出範圍 | EArgumentOutOfRangeException | 插入允許 Index=Length,刪除需 Index+Count ≤ Length |
| 迴圈中 Insert 導致索引錯亂 | 漏插/重複插入 | 從後往前處理,或記錄位移量補償 |
| Remove(0, Length) 清空 | 可行但非慣用 | 用 Length := 0 或 Clear |
| Insert 大量小字串 | 頻繁擴容移動 | 預分配 EnsureCapacity 或批次組裝 |
最佳實務建議
- 大量編輯前預分配:
sb.EnsureCapacity(預估最終長度)
- 批次插入用 Append + 最後調整:而非多次 Insert
- 替換操作用 Replace 方法:內部優化優於手動 Remove+Insert
- 索引計算用 Length/Length():避免硬編碼魔術數字
- 配合 Chars[] 進行字元級精細操作:單字元修改無移動開銷
總結
TStringBuilder.Insert 與 Remove 以緩衝區移動取代字串重新分配,為動態字串編輯提供 O(n) 且無記憶體碎片的高效方案。掌握 Index 0-based 語意、Count 範圍邊界、EnsureCapacity 預分配技巧,配合 Replace、Chars[] 等輔助方法,即能優雅實現模板填充、報表建構、原地修剪等進階編輯場景。