【问题标题】:Delphi RichEdit find line containing string but not after parenthesesDelphi RichEdit 查找包含字符串但不在括号后的行
【发布时间】:2013-09-01 22:30:12
【问题描述】:

我正在尝试获取一个例程,该例程将找到一个不跟随括号的字符串。例如,如果在 RichEdit 中打开的文件包含这些 CNC 代码行,我希望它找到前两行并忽略第三行。在第二行中,它应该只找到并突出显示搜索字符串的第一次出现。本例中的搜索字符串 (mach.TOOL_CHANGE_CALL) 为 'T'。

N1T1M6
N1T1M6(1/4-20 TAP .5 DP.)
(1/4-20 TAP .5 DP.)

我已经走到这一步了,但我很难过。

procedure TMainForm.ToolButton3Click(Sender: TObject); // find tool number
var
  row:integer;
  sel_str:string;
  par:integer;
  tool:integer;
  tool_flag:integer ;
  line_counter:integer;
  tool_pos:integer;
  line_begin:integer;
  RE:TRichEdit;
begin
  RE:=(ActiveMDIChild as TMDIChild).RichEdit1;
  line_counter:=0;
  tool_flag:=0;
  tool_pos:=0;

  row:=SendMessage(RE.Handle,EM_LINEFROMCHAR,-1, RE.SelStart);

  while  tool_flag =0 do    
  begin
    RE.Perform(EM_LINESCROLL,0,line_counter);
    sel_str := RE.Lines[Line_counter];
    tool:=pos(mach.TOOL_CHANGE_CALL,sel_str);
    par:=pos('(',sel_str);
    if par=0 then 
      par:=pos('[',sel_str);
    tool_pos:=tool_pos+length(sel_str);

    if (tool>0) and (par = 0)  then
    begin
      RE.SetFocus;
      tool_pos:=tool_pos + line_counter-1;
      line_begin:=tool_pos-tool;
      RE.SelStart := line_begin;
      RE.SelLength := Length(sel_str);
      tool_flag:=1;
    end;
    inc (line_counter);
  end;
end;

我得到的结果是它会忽略第三个字符串,但也会忽略第二个字符串。它也不会在文件中找到随后出现的字符串,它只是从文本的开头重新开始并再次找到第一个。如何让它找到第二个示例,然后在下次单击按钮时找到下一个“T”?我还需要它来突出显示搜索字符串所在的整行。

【问题讨论】:

  • 我不确定我是否理解您的要求。如果您的搜索表达式是“T”,您的建议是只匹配两次出现的“N1T1M6”,而不匹配任何一次出现的TAP
  • 正确,它应该只找到不在括号内的 T。在 CNC 代码中,括号内的文本是 cmets。我试图仅在代码中找到实际工具('T')更改位置,并且始终位于 cmets 之外。它也找不到任何其他在括号内包含 T 的单词。
  • 最后一个信息请求。测试是否如您列出的那样(每行一个“命令”)?或者它是一个连续的文本块?恐怕我对CNC代码不熟悉。如果每行一个命令,用正则表达式应该很容易处理。
  • 不太确定我是否遵循了这个问题。但我会试一试。 CNC 代码包含多行文本,有时甚至达到数百万行。我们需要能够在所有这些行中快速找到换刀位置 ('T')。这些工具更改调用可能多达 60 个,但它们看起来总是与我给您的示例相似。有时描述工具的注释会在换刀调用之前的行 ('T') 有时在同一行,有时在之后的行。所以我需要忽略那些注释行,即使它们包含 T
  • 如果找到 'par',您可以搜索匹配的 par-close (?),然后忽略中间的匹配。您可以使用 PosEx 从某个位置开始搜索。

标签: delphi richedit


【解决方案1】:

鉴于您发布的示例,您可以使用 Delphi(XE 和更高版本)正则表达式来匹配您指定的文本。在这里,我将您显示的三个示例行放入TMemo(下面的代码中的Memo1),评估正则表达式,并将找到的匹配项放入Memo2 - 只要您的@987654329 @ 仅包含纯文本,您可以通过将 Memo1Memo2 分别替换为 RichEdit1RichEdit2 来使用相同的代码。

我已经更新了两个 sn-ps 中的代码,以显示如何获取匹配结果的确切位置(作为与第一个字符的偏移量)和长度;您可以使用 SelStartSelLength 在 Richedit 中突出显示匹配项。

uses
  RegularExpressions;

procedure TForm1.Button1Click(Sender: TObject);
var
    Regex: TRegEx;
    MatchResult: TMatch;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.Add('N1T1M6');
  Memo1.Lines.Add('N1T1M6(1/4-20 TAP .5 DP.)');
  Memo1.Lines.Add('(1/4-20 TAP .5 DP.)');
  Memo2.Clear;
  // See the text below for an explanation of the regular expression
  Regex := TRegEx.Create('^\w+T\w+', [roMultiLine]);
  MatchResult := Regex.Match(Memo1.Lines.Text);
  while MatchResult.Success do 
  begin
    Memo2.Lines.Add(MatchResult.Value +
                  ' Index: ' + IntToStr(MatchResult.Index) +
                  ' Length: ' + IntToStr(MatchResult.Length));
    MatchResult := MatchResult.NextMatch;
  end;
end;

这会产生以下结果:

如果您使用的是不支持正则表达式的 Delphi 版本,您可以使用免费的 TPerlRegEx 并进行一些小的代码更改以产生相同的结果:

uses
  PerlRegEx;

procedure TForm1.Button1Click(Sender: TObject);
var
  Regex: TPerlRegEx;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.Add('N1T1M6');
  Memo1.Lines.Add('N1T1M6(1/4-20 TAP .5 DP.)');
  Memo1.Lines.Add('(1/4-20 TAP .5 DP.)');
  Memo2.Clear;
  Regex := TPerlRegEx.Create;
  try
    Regex.RegEx := '^\w+T\w+';
    Regex.Options := [preMultiLine];
    Regex.Subject := Memo1.Lines.Text;
    if Regex.Match then 
    begin
      repeat
        Memo2.Lines.Add(Regex.MatchedText +
                        ' Offset: ' + IntToStr(Regex.MatchedOffset) +
                        ' Length: ' + IntToStr(Regex.MatchedLength));
      until not Regex.MatchAgain;
    end;
  finally
    Regex.Free;
  end;
end;

上面的正则表达式(^\w+T\w+)的意思是:

选项: ^ 和 $ 匹配换行符 在行首断言位置(在开头 字符串或换行符之后)«^» 匹配作为“单词字符”的单个字符(字母、 数字和下划线)«\w+» 在一次和无限次之间,尽可能多次, 根据需要回馈(贪婪)«+» 匹配字符“T”字面意思«T» 匹配作为“单词字符”的单个字符(字母、 数字和下划线)«\w+» 在一次和无限次之间,尽可能多次, 根据需要回馈(贪婪)«+» 使用 RegexBuddy 创建

你可以找到一个关于正则表达式的不错的教程here。我用于计算正则表达式的工具(实际上为这两个示例生成了大部分 Delphi 代码)是RegexBuddy - 我不隶属于生产它的公司,而只是该产品的用户。

【讨论】:

  • 你能解释一下这行吗:Regex.RegEx := '^\w+T\w+'; ?我假设 ^ 表示一个指针,但我不明白该行如何排除括号中的内容。我确定您的代码会起作用,我想了解原因,这样我就不必问更多问题了。
  • 不,它不是指针。它是正则表达式的一部分;它是行首的锚点。我将进行编辑以提供有关正则表达式含义的更多信息,以及指向包含更多信息的页面的链接。
  • 如果我理解 TPerlRegEx ,它会在文件中查找 T 的每个实例。我基本上只是想在 RichEdit 中找到一个不在括号内的 T 实例,将插入符号设置在该位置并突出显示该行。然后下次按下按钮时,找到下一次出现再次设置插入符号并在按下按钮时将其突出显示与文件中的工具 (T) 一样多。
  • 不,它不是这样做的。请参阅我发布的解释。您可以通过使用 TRegex (或 TPerlRegex 返回匹配文本的偏移量的属性来做到这一点。(我发布的正则表达式符合您的条件 - 鉴于您发布的三行文本,它匹配前两行(在第二个中,直到但不包括开头 ( 的部分),但与第二行 TAP 中的 T 或第 3 行中的任何内容不匹配,就像屏幕图像中的右侧备忘录一样表示。)正则表达式比Pos更强大,可以匹配满足相当复杂条件的文本。
  • @Martyn: Here's one 匹配一个 5 位数字,一个 :,然后是一个 5 位数字,并确保第二个数字大于第一个。
猜你喜欢
  • 2019-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-06
  • 1970-01-01
  • 1970-01-01
  • 2015-10-01
  • 1970-01-01
相关资源
最近更新 更多