如何在定界符后提取子字符串但如果在两个标签之间找到则忽略?

How to Extract sub-string after delimiter but ignore if found between two tags?

我需要在特定分隔符之后提取子字符串,但如果指定的分隔符在其他两个标记之间,则应忽略它。

例如,拿这个测试字符串:

The quick <"@brown fox"> jumps over the lazy dog. The quick @brown fox jumps over the lazy dog

所需的输出将是:

brown fox jumps over the lazy dog

这是因为第一个找到的@定界符在两个“”之间,所以应该忽略,第二个@定界符不在“”里面,所以应该提取后面的文本。

我可以通过使用 Pos 并提取其右侧的文本来找到 @ 定界符的起始位置,如下所示:

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
  I: Integer;
begin
  S := 'The quick <"@brown fox"> jumps over the lazy dog. The quick @brown fox jumps over the lazy dog';
  I := Pos('@', S);
  if I > 0 then
  begin
    ShowMessage(Copy(S, I, Length(S)));
  end;
end;

然而,这将始终找到第一个@分隔符,无论它是否被两个“”包围。上面的结果是:

@brown fox"> jumps over the lazy dog. The quick @brown fox jumps over the lazy dog

期望的结果应该是:

brown fox jumps over the lazy dog

如果分隔符位于两个“”标记之间,我如何更改代码以在使用 Pos 时忽略 @ 分隔符?我只想找到第一个@分隔符,然后复制文本。

在找到第一个有效的之后是否还有其他@分隔符也没有关系,例如这也应该有效:

The quick <"@brown fox"> jumps over the lazy dog. The quick @brown fox jumps@ ov@er the lazy@ dog

应该还是return:

brown fox jumps over the lazy dog

因为我们只对第一个有效的@定界符感兴趣,忽略后面的任何其他内容并忽略两个“”标记之间的任何内容。

请注意,尽管我已经标记 Delphi 我确实主要使用 Lazarus,所以理想情况下我需要帮助想出一个不使用字符串助手等魔法帮助的解决方案。

谢谢。

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
  L, I: Integer;
  take : Boolean;
begin
  S := 'The quick <"@brown fox"> jumps over the lazy dog. The quick @brown fox jumps over the lazy dog';
  L := Length(S);
  I := Pos('@', S);

  while I > 0 do
  begin
    take := True;
    if I > 1 then take := S[I-1] <> '"';
    if take then begin
      if I < L then
        ShowMessage(Copy(S, I + 1, L));
      Break;
    end;
    S[I] := '_';
    I := Pos('@', S);
  end;
end;

要查明 @ 是否不在 " 封闭标记内,请从头开始解析字符串。

如果在开始标记后发现定界符,但没有结束标记,此例程也会提取结果。

function ExtractString(const s: String): String;
var
  tagOpen: Boolean;
  delimiterPos,i,j: Integer;
begin
  tagOpen := false;
  delimiterPos := 0;
  Result := '';
  for i := 1 to Length(s) do begin
    if (s[i] = '"') then begin
      tagOpen := not tagOpen;
      delimiterPos := 0;
    end
    else begin
      if (s[i] = '@') then begin
        if (delimiterPos = 0) then
          delimiterPos := i;
        if not tagOpen then // Found answer
          Break;
      end;
    end;         
  end;

  // If there is no closing tag and a delimiter is found
  // since the last opening tag, deliver a result. 
  if (delimiterPos > 0) then begin
    // Finally extract the string and remove all `@` delimiters.
    SetLength(Result,Length(s)-delimiterPos);
    j := 0;
    for i := 1 to Length(Result) do begin
      Inc(delimiterPos);
      if (s[delimiterPos] <> '@') then begin
        Inc(j);
        Result[j] := s[delimiterPos];
      end;
    end;
    SetLength(Result,j);      
  end;
end;