我有一个TMemo
,它显示查询中的文本。我想删除'{'
和'}'
之间的所有字符,这样这个字符串'{color:black}😊{color}{color:black}{color}'
就会像这个😊
一样结束。
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在RTL中提供了这种表达式。
s := TRegEx.Replace('{color:black}😊{color}{color:black}{color}', '{.*?}', '', []);
或者使用备忘录:
MemoComments.Lines.Text := TRegEx.Replace(MemoComments.Lines.Text, '{.*?}', '', []);
在这个表达式中,{.*?}
,.*?
表示任何字符(.
(的任何数字(*
(,但尽可能少以匹配表达式(*?
(的其余部分。最后一点非常有力。默认情况下,正则表达式是"贪婪"的,这意味着.*
将尽可能多地匹配字符,因此它将占用最后一个}
之前的所有字符,包括微笑符号和其间的所有其他颜色代码。
陷阱/缺点
和Andreas一样,我也不太喜欢正则表达式。尴尬的语法可能很难解密,尤其是如果你不经常使用它们的话。
此外,一个看似简单的正则表达式可能很难执行,有时会非常慢,尤其是在处理较大字符串时。我最近遇到了一个非常神奇的,它在验证一个大约1000个字符的字符串是否符合某个模式时被卡住了好几分钟。
所使用的表达式实际上就是一个例子。它必须在.*?
部分之后向前看,以检查它是否已经满足表达式的其余部分。如果没有,那就回去,换一个角色,再向前看。对于这个表达式,这不是问题,但如果一个表达式有多个可变长度的部分,这可能是一个CPU密集型的过程!
我的早期版本{[^}]*}
至少在理论上更有效,因为它只匹配所有不是}
的字符,而不是任何字符。更容易执行,但更难阅读。在上面的回答中,我倾向于可读性而非性能,但这始终是需要记住的。
请注意,我的第一个版本{[^}]*}
看起来更加复杂。我使用来转义括号,因为它们对分组也有特殊的含义,但在这种情况下似乎没有必要。
最后,有不同的正则表达式方言,这也没有帮助。
也就是说
幸运的是,Delphi封装了PCRE库,它是开源的、高度优化的、维护良好的、文档化的,并实现了最常用的方言。
对于这样的操作,它们可以简洁易写,使用速度足够快,如果你更频繁地使用它们,读写它们也会变得更容易,尤其是如果你使用regex101.com这样的工具,你可以在那里试用和调试regex。