2012-09-11 17 views
6

Jak mogę sprawdzić, czy ciąg zawiera podłańcuch, ale tylko w określonej pozycji?Sprawdź, czy ciąg zawiera słowo, ale tylko w określonej pozycji?

Przykład ciąg:

Jaki jest Twój ulubionykolor? moja [ulubiony] kolor niebieski

Gdybym chciał sprawdzić, czy ciąg zawierał konkretne słowo I zazwyczaj to zrobić:

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

Co potrzebne jest, aby określić, czy słowo ulubiony istnieje w ciągu znaków, ignorując jednak, jeśli pojawia się wewnątrz symboli [], czego powyższy przykładowy kod wyraźnie nie robi.

Jeśli więc umieścić kod w funkcji logicznej, niektóre wyniki próbki wyglądałby następująco:

TRUE: Jaki jest Twój ulubionykolor? moja [mój ulubiony] kolor jest niebieski

TRUE: Jaki jest Twój ulubionykolor? moja [bla bla] kolor jest niebieski

FAŁSZ: Jaki jest Twój bla bla kolor? moja [jakiś ulubiony] kolor niebieski

Pierwsze dwie próbki powyżej są prawdziwe, ponieważ ulubione słowo znajduje się poza [] symboli, czy to wewnątrz, czy nie.

Trzecia próbka jest fałszywa, ponieważ nawet jeśli istnieje słowo ulubione, pojawia się tylko wewnątrz symboli [] - powinniśmy tylko sprawdzić, czy istnieje poza symbolami.

Potrzebuję więc funkcji do określenia, czy słowo (ulubione w tym przykładzie) pojawia się w ciągu znaków, ale ignorowanie faktu, jeśli słowo jest otoczone wewnątrz symbolami [].

+0

Pomysły: 1) Podziel ciąg na '\ s' i sprawdź 4. element. 2) Sprawdź "ulubione" (zwróć uwagę na spacje wokół słowa). 3) Podziel ciąg znaków na znak zapytania '?', A następnie sprawdź, czy zawiera 'ulubiony'. –

+5

Wyszukaj "[", jeśli znaleziono wyszukiwanie "]", usuń pośrednie, w tym "[]", wyszukaj ulubione. –

+0

Lub użyj wyrażenia regularnego (z jednostkami zawartymi w XE) z wyrażeniem "'\ b" + wordtofind +' \ b''. Jedyne, na co musisz uważać, to "Czy to twoje ulubione?", Gdzie "?" Spowodowałoby, że nie byłoby to zgodne. –

Odpowiedz

8

Podoba mi się Sertac's idea o usunięciu ciągów ujętych w nawiasy i poszukiwanie ciągu znaków. Oto przykładowy kod przedłużony o poszukiwaniu całych wyrazów i wielkości liter:

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; 

tutaj jest zoptymalizowana wersja funkcji, która wykorzystuje wskaźnik char iteracji bez manipulacji strun. W porównaniu z poprzednią wersją obsługuje to przypadek, gdy masz ciąg z brakującym nawiasem zamykającym, jak na przykład My [favorite color is. Taki ciąg jest oceniany jako True ze względu na brakujący nawias.

Zasada polega na przejściu całego ciągu znaków przez znak char, a gdy znajdziesz nawias otwierający, sprawdź, czy wspornik ma dla siebie parę zamykającą. Jeśli tak, sprawdź, czy podciąg z zapisanej pozycji, dopóki nawias otwierający nie zawiera szukanego słowa. Jeśli tak, opuść funkcję. Jeśli nie, przenieś zapisaną pozycję do wspornika zamykającego. Jeśli nawias otwierający nie ma własnej pary zamykającej, wyszukaj słowo z zapisanej pozycji na końcu całego ciągu znaków i wyjdź z funkcji.

Dla skomentował wersji tego kodu 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; 
+1

Świetna odpowiedź, szczególnie przydatny jest link do odpowiedzi z komentarzami, sprawia, że ​​jest to nieco łatwiejsze do strawienia i zrozumieć, co się dzieje. –

+1

Dzięki! W każdym razie, regex jest właściwym sposobem na zrobienie tego, czego potrzebujesz (i na pewno łatwiejszym), ale z drugiej strony jest to bardziej proste tylko do tego konkretnego zadania (i bardziej wydajne, powiedziałbym, ponieważ regex przynajmniej musi przeanalizować wyrażenie przed rozpoczęciem dopasowywania). Powiedziałbym, że jeśli nie zamierzasz budować jakiegoś parsera na przykład, gdzie miałbyś wiele podobnych zadań, takich jak ten mecz, to rozwiązanie może być lżejsze niż w tym regex. Ale głównym powodem, dla którego napisałem, jest to, że żadna z tych odpowiedzi nie używała czystego Delphi. – TLama

7

W regular expressions można użyć rzeczy o nazwie look-around. W twoim przypadku możesz rozwiązać to z negatywnym lookbehind: chcesz "ulubiony", chyba że jest poprzedzony nawiasem otwierającym. To może wyglądać następująco:

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

Krok po kroku: (?<! jest ujemny lookbehind prefiks, szukamy \[ ewentualnie następuje żadna lub więcej rzeczy, które nie są otwarcie lub zamknięcie klamry: [^\[\]]* zamknąć negatywna lookbehind z ), a następnie favorite zaraz po.

+0

Myślę, że twoje jest eleganckie i właściwe rozwiązanie – diegoaguilar

0

Myślę, że możesz przerobić swój problem jako "znaleźć przewinięcie podanego ciągu nie będąc otoczonym nawiasami kwadratowymi". Jeśli to opisuje Twój problem, możesz użyć prostego wyrażenia regularnego, takiego jak [^\[]favorite[^\]].

Powiązane problemy