6

文字列に部分文字列が含まれているが、特定の位置にのみ含まれているかどうかを確認するにはどうすればよいですか?

文字列の例:

あなたの好きな色は何ですか?私の[好きな]色は青です

文字列に特定の単語が含まれているかどうかを確認したい場合は、通常、次のようにします。

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

私が必要としているのは、お気に入りという単語が文字列に存在するかどうかを判断することです。ただし、[ ] 記号内に表示されている場合は無視しますが、上記のコード サンプルでは明らかに実行されていません。

したがって、コードをブール関数に入れると、サンプル結果は次のようになります。

TRUE:好きな色は何ですか? 私の[私の好きな]色は青です

TRUE:好きな色は何ですか? 私の [何とか] 色は青です

FALSE: あなたの好きな色は何ですか? 私の[あるお気に入り]の色は青です

上記の最初の 2 つのサンプルは、お気に入りという単語が [ ] 記号の外側にあるため (内側にあるかどうかに関係なく) 真です。

3 番目のサンプルは false です。なぜなら、お気に入りという単語があっても、[ ] 記号の内側にしか現れないためです。記号の外側に存在するかどうかのみを確認する必要があります。

したがって、単語 (この例ではお気に入り) が文字列に含まれているかどうかを判断する関数が必要ですが、単語が [ ] 記号で囲まれている場合は無視されます。

4

3 に答える 3

8

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;

これは、文字列操作なしでポインター char 反復を使用する関数の最適化されたバージョンです。以前のバージョンと比較して、これは、たとえば のような閉じ括弧が欠落している文字列がある場合を処理しますMy [favorite color is。そのような文字列は、ブラケットがないため、True と評価されます。

原則として、文字列全体を char ごとに調べ、開き括弧が見つかったら、その括弧自体に閉じペアがあるかどうかを調べます。はいの場合は、保存された位置から開き括弧までの部分文字列に検索された単語が含まれているかどうかを確認します。はいの場合、関数を終了します。そうでない場合は、保存された位置を閉じ括弧に移動します。開き括弧に独自の閉じペアがない場合は、保存された位置から文字列全体の末尾まで単語を検索し、関数を終了します。

このコードのコメント版については、このリンクをたどってください

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;
于 2012-09-12T10:54:23.943 に答える
7

正規表現には、使用できるルックアラウンドと呼ばれるものがあります。あなたの場合、否定的な後読みで解決できます。開始ブラケットが前にない限り、「お気に入り」が必要です。次のようになります。

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

Step by step:は負の後読みプレフィックスです。必要に応じて、括弧を閉じたり開いたりしていないものが 1 つ以上続くもの(?<!を探します: 、負の後読みを で閉じ、その直後。\[[^\[\]]*)favorite

于 2012-09-11T19:31:32.650 に答える
0

問題を「角かっこで囲まれていない提供された文字列の出現を見つける」と言い換えることができると思います。それがあなたの問題を説明しているなら、あなたは先に進んで、のような単純な正規表現を使うことができます[^\[]favorite[^\]]

于 2012-09-11T22:06:38.260 に答える