【问题标题】:Check if a string contains a word but only in specific position?检查字符串是否包含单词但仅在特定位置?
【发布时间】:2012-09-11 18:39:11
【问题描述】:

如何检查字符串是否包含子字符串,但仅在特定位置?

示例字符串:

最喜欢的颜色是什么?我的[最喜欢的]颜色是蓝色

如果我想检查字符串是否包含特定单词,我通常会这样做:

var
  S: string;
begin
  S := 'What is your favorite color? my [favorite] color is blue';
  if (Pos('favorite', S) > 0) then
  begin
    //
  end;
end;

我需要确定字符串中是否存在 favorite 这个词,忽略它是否出现在 [ ] 符号内,而上面的代码示例显然没有这样做。

因此,如果我们将代码放入布尔函数中,一些示例结果将如下所示:

TRUE:您最喜欢的颜色是什么?我的[我最喜欢的]颜色是蓝色

TRUE:您最喜欢的颜色是什么?我的 [blah blah] 颜色是蓝色

错误:你的 blah blah 是什么颜色?我的[一些最喜欢的]颜色是蓝色

上面的前两个示例是正确的,因为在 [ ] 符号之外发现了 favourite 这个词,无论它是否在其中。

第三个样本是假的,因为即使有 favourite 这个词,它也只出现在 [ ] 符号内 - 我们应该只检查它是否存在于符号之外。

所以我需要一个函数来确定一个单词(在这个例子中是最喜欢的)是否出现在字符串中,但是忽略这个单词是否被 [ ] 符号包围的事实。

【问题讨论】:

  • 想法:1) 拆分\s 上的字符串并检查第4 个元素。 2)检查`喜爱`(注意单词周围的空格)。 3)拆分问号?上的字符串,然后检查它是否包含favorite
  • 搜索'[',如果找到搜索']',删除中间包括'[]',搜索收藏夹。
  • 或者使用带有'\b' + wordtofind + '\b' 正则表达式的正则表达式(XE 中包含单位)。唯一需要注意的是Is it your favorite?? 会使其不匹配。
  • 为了扩展@Ken 关于正则表达式的想法,this 可能是一个灵感。

标签: string delphi delphi-xe


【解决方案1】:

我喜欢Sertac's idea 删除括号括起来的字符串并在此之后搜索字符串。这是一个通过搜索整个单词和区分大小写而扩展的代码示例:

function ContainsWord(const AText, AWord: string; AWholeWord: Boolean = True;
  ACaseSensitive: Boolean = False): Boolean;
var
  S: string;
  BracketEnd: Integer;
  BracketStart: Integer;
  SearchOptions: TStringSearchOptions;
begin
  S := AText;
  BracketEnd := Pos(']', S);
  BracketStart := Pos('[', S);
  while (BracketStart > 0) and (BracketEnd > 0) do
  begin
    Delete(S, BracketStart, BracketEnd - BracketStart + 1);
    BracketEnd := Pos(']', S);
    BracketStart := Pos('[', S);
  end;
  SearchOptions := [soDown];
  if AWholeWord then
    Include(SearchOptions, soWholeWord);
  if ACaseSensitive then
    Include(SearchOptions, soMatchCase);
  Result := Assigned(SearchBuf(PChar(S), StrLen(PChar(S)), 0, 0, AWord,
    SearchOptions));
end;

这是该函数的优化版本,它使用指针字符迭代而不进行字符串操作。与以前的版本相比,它可以处理缺少右括号的字符串的情况,例如My [favorite color is。由于缺少括号,因此该字符串被评估为 True。

原则是一个字符一个字符地遍历整个字符串,当你找到左括号时,看看那个括号是否有一个闭对。如果是,则检查从存储位置到左括号的子字符串是否包含搜索到的单词。如果是,则退出该功能。如果没有,请将存储的位置移动到右括号。如果左括号没有自己的右括号,则从存储位置到整个字符串的末尾搜索单词并退出函数。

此代码的注释版本follow this link

function ContainsWord(const AText, AWord: string; AWholeWord: Boolean = True;
  ACaseSensitive: Boolean = False): Boolean;
var
  CurrChr: PChar;
  TokenChr: PChar;
  TokenLen: Integer;
  SubstrChr: PChar;
  SubstrLen: Integer;
  SearchOptions: TStringSearchOptions;
begin
  Result := False;
  if (Length(AText) = 0) or (Length(AWord) = 0) then
    Exit;
  SearchOptions := [soDown];
  if AWholeWord then
    Include(SearchOptions, soWholeWord);
  if ACaseSensitive then
    Include(SearchOptions, soMatchCase);
  CurrChr := PChar(AText);
  SubstrChr := CurrChr;
  SubstrLen := 0;
  while CurrChr^ <> #0 do
  begin
    if CurrChr^ = '[' then
    begin
      TokenChr := CurrChr;
      TokenLen := 0;
      while (TokenChr^ <> #0) and (TokenChr^ <> ']') do
      begin
        Inc(TokenChr);
        Inc(TokenLen);
      end;
      if TokenChr^ = #0 then
        SubstrLen := SubstrLen + TokenLen;
      Result := Assigned(SearchBuf(SubstrChr, SubstrLen, 0, 0, AWord,
        SearchOptions));
      if Result or (TokenChr^ = #0) then
        Exit;
      CurrChr := TokenChr;
      SubstrChr := CurrChr;
      SubstrLen := 0;
    end
    else
    begin
      Inc(CurrChr);
      Inc(SubstrLen);
    end;
  end;
  Result := Assigned(SearchBuf(SubstrChr, SubstrLen, 0, 0, AWord,
    SearchOptions));
end;

【讨论】:

  • 很好的答案,特别有用的是与 cmets 的答案的链接,可以更容易地消化和理解正在发生的事情。
  • 谢谢!无论如何,正则表达式是做你需要的正确方法(当然更容易),但另一方面,这更直接地处理这个特定任务(我想说更有效,因为正则表达式至少需要解析开始匹配之前的表达式)。我会说,如果您不打算构建一些解析器,那么您在哪里会有很多类似的任务,比如这个匹配,那么这个解决方案可能比包含正则表达式更轻。但主要原因是,我发布这个的主要原因是这里的答案都没有使用纯 Delphi。
【解决方案2】:

regular expressions 中有一个叫look-around 的东西你可以使用。在你的情况下,你可以用消极的后视来解决它:你想要“最喜欢的”,除非它前面有一个左括号。它可能看起来像这样:

(?<!\[[^\[\]]*)favorite

一步一步:(?&lt;! 是否定的lookbehind 前缀,我们正在寻找\[ 可选地后跟一个或多个不是右括号或左括号的东西:[^\[\]]*,用@987654327 关闭否定的lookbehind @,然后是favorite

【讨论】:

  • 我认为你的解决方案是优雅而恰当的
【解决方案3】:

我认为您可以将您的问题改写为“发现所提供的字符串没有被方括号包围”。如果这描述了您的问题,那么您可以继续使用简单的正则表达式,例如 [^\[]favorite[^\]]

【讨论】:

    猜你喜欢
    • 2013-12-23
    • 1970-01-01
    • 2011-05-20
    • 2023-04-01
    • 1970-01-01
    相关资源
    最近更新 更多