Delphi String替换两个字符之间的字符串

Delphi StringReplace a string between two chars

我有一个 TMemo 显示来自查询的文本。我想删除 '{''}' 之间的所有字符,所以这个字符串 '{color:black}{color}{color:black}{color}' 最终会像这样 </code>.</p> <pre><code>MemoComments.Lines.Text := StringReplace(MemoComments.Lines.Text, '{'+ * +'}', '', rfReplaceAll);

我知道我代码中的*是错误的。它只是一个占位符。我怎样才能以正确的方式做到这一点?

这可能吗,还是我必须创建一个复杂的循环?

在这种情况下,您可以使用正则表达式。我相信很快就会有人为您发布这样的答案。

但是,为了完整起见,我想表明基于循环的方法一点也不复杂,而是相当简单:

function ExtractContent(const S: string): string;
var
  i, c: Integer;
  InBracket: Boolean;
begin
  SetLength(Result, S.Length);
  InBracket := False;
  c := 0;
  for i := 1 to S.Length do
  begin
    if S[i] = '{' then
      InBracket := True
    else if S[i]= '}' then
      InBracket := False
    else if not InBracket then
    begin
      Inc(c);
      Result[c] := S[i];
    end;
  end;
  SetLength(Result, c);
end;

请注意,我避免了不必要的堆分配。

(就我个人而言,我从来都不是正则表达式的忠实粉丝。对我来说,上述算法的正确性是显而易见的,它只能以一种方式解释,而且显然是以一种高性能的方式编写的。另一方面,正则表达式有点像“魔术”。但我承认我有点像恐龙。)

看起来你想要一种正则表达式,Delphi 幸运的是 offers in their RTL

s := TRegEx.Replace('{color:black}{color}{color:black}{color}', '{.*?}', '', []);

或使用备忘录:

MemoComments.Lines.Text := TRegEx.Replace(MemoComments.Lines.Text, '{.*?}', '', []);

在此表达式中,{.*?}.*?表示任意字符(.)的任意数字(*),但最少为可能匹配表达式的其余部分 (*?)。最后一点非常强大。默认情况下,正则表达式是 'greedy',这意味着 .* 会匹配尽可能多的字符,所以它将把所有内容都保留到最后一个 },包括笑脸和所有其他字符中间的颜色代码。

Pitfalls/cons

和 Andreas 一样,我也不喜欢正则表达式。笨拙的语法很难破译,特别是如果你不经常使用它们的话。

此外,看似简单的正则表达式可能很难 执行 有时它实际上非常慢,尤其是在处理较大的字符串时。我最近遇到了一个非常神奇的东西,它在验证一个大约 1000 个字符的字符串是否与某个模式匹配时卡住了几分钟。

所使用的表达式实际上就是一个例子。它必须在 .*? 部分之后向前看,以检查它是否已经满足表达式的其余部分。如果没有,就回去,换一个角色,再往前看。对于这个表达式,这不是问题,但如果一个表达式有多个可变长度的部分,这可能是一个 CPU 密集过程!

我的早期版本 {[^}]*} 至少在理论上更有效,因为它不是 任何 字符,而是匹配所有不是 [= 的字符18=]。更容易执行,但更难阅读。在上面的答案中,我追求的是可读性而不是性能,但始终要牢记这一点。

请注意,我的第一个版本 \{[^\}]*\} 看起来更加复杂。我使用 \ 来转义括号,因为它们对于分组也有特殊含义,但在这种情况下似乎没有必要。

最后,有不同的正则表达式方言,这也没有帮助。

就是这样

幸运的是 Delphi 包装了 PCRE library,它是开源的、高度优化的、维护良好的、有据可查的,并且实现了最常用的方言。

对于像这样的操作,它们可以简洁易写,使用起来足够快,如果你更频繁地使用它们,它也会变得更容易读写它们,特别是如果您使用像 regex101.com 这样的工具,您可以在其中尝试和调试正则表达式。