簡體   English   中英

如何在字符串中搜索子字符串的許多變體

[英]How to search for many variations of a substring in string

我試圖在一個字符串中搜索一個子字符串,但是必須有一個更有效的方法然后這個..

      //search for volume
     if AnsiContainsStr(SearchString, 'v1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'V1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'Volume1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'Volume 1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'Vol1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'vol1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'Vol 1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'vol 1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'Vol.1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'vol.1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'Vol. 1') then
         Volume := '1';
     if AnsiContainsStr(SearchString, 'vol. 1') then
         Volume := '1';


     if AnsiContainsStr(SearchString, 'v2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'V2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'Volume2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'Volume 2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'Vol2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'vol2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'Vol 2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'vol 2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'Vol.2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'vol.2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'Vol. 2') then
         Volume := '2';
     if AnsiContainsStr(SearchString, 'vol. 2') then
         Volume := '2';

由於您使用XE2對其進行了標記,因此您可以使用正則表達式輕松進行匹配

  var
     Regex: String;
  begin
     Regex := '^[v](ol\.?|olume)?\s*(1|\.\s*1)$';
     if TRegEx.IsMatch(SearchString, Regex, [roIgnoreCase]) then
        Volume := '1'
     Regex := '^[v](ol\.?|olume)?\s*(2|\.\s*2)$';
     if TRegEx.IsMatch(SearchString, Regex, [roIgnoreCase]) then
        Volume := '2'
  end;

現在,我不是最好的設計正則表達式,但我測試了上面的那個,它似乎匹配你所有的變化(也許其他人可以想出一個更簡潔的)。

對於很多字符串和頻繁搜索,使用后綴樹將是您最好的選擇。 否則使用正則表達式的更簡單方法也可以提供幫助,您的字符串看起來足夠規

建立在@ user582118的答案上:

如果使用^v(ol\\.?|olume)?\\s*([0-9]+)$作為RegEx模式,則不必嘗試每個可能的數值。 它將與最后的一個或多個數字字符匹配。 然后,您可以使用TMatchValueGroups屬性從字符串中提取數字。

var
  RegEx: TRegEx; // This is a record, not a class, and doesn't need to be freed!
  Match: TMatch;
  i: Integer;
begin
  RegEx := TRegEx.Create('^v(ol\.?|olume)?\s*([0-9]+)$');
  Match := RegEx.Match('vol.3456');
  WriteLn('Value: ' + Match.Value);
  for i := 0 to Match.Groups.Count - 1 do
    WriteLn('Group', i, ': ', Match.Groups[i].Value);
end;

得到:

Value: vol.3456
Group0: vol.3456
Group1: ol.
Group2: 3456

嘗試這樣的事情:

const
  Prefixes: array[0..6] of String = (
    'VOLUME '
    'VOLUME'
    'VOL. '
    'VOL '
    'VOL.'
    'VOL'
    'V'
  );

var
  S: String;
  P: PChar;
  I, J, Len: Integer;
  Volume: Char;
begin
  Volume = #0;
  S := UpperCase(SearchString);
  P := PChar(S);
  Len := Length(S);
  I := 1;
  while (Len > 0) and (Volume = #0) do
  begin
    if (P^ <> 'V') then begin
      Inc(P);
      Dec(Len);
      Continue;
    end;
    for J := Low(Prefixes) to High(Prefixes) do
    begin
      if AnsiStrLComp(P, PChar(Prefixes[J]), Length(Prefixes[J])) = 0 then
      begin
        Inc(P, Length(Prefixes[J]));
        Dec(Len, Length(Prefixes[J]));
        if (Len > 0) then begin
          if (P^ >= '1') and (P^ <= '7') then
            Volume := P^;
        end;
        Break;
      end;
    end;
  end;
end;

為了比較郵件地址,我不得不做類似的事情。 我刪除了空格和標點符號。 然后我使用了CompareText,因此它不區分大小寫。

很多你的If語句都涉及比較可能有或沒有“Vol”或“Volume”之間的句號或空格的字符串和數字。 刪除句點和空格,每個卷號留下兩個If語句:一個用於VOL,一個用於VOLUME。 您甚至可以通過將“volume”替換為“vol”來將每個卷減少到一個If語句。

首先使您的搜索字符串大寫(一次),然后針對搜索字符串的大寫版本執行每個檢查。 這樣可以將檢查次數減少一半而不需要不區分大小寫的搜索(這可能會每次都改變兩個字符串的大小寫)。

您可以更進一步,使用JCL中的一個通配符匹配函數,例如StrMatches。 然而,雖然這會減少代碼行數,但它不能像具有特定匹配一樣快。

如果您希望為Volume創建許多不同的值,請編寫自己的函數以搜索字符串的字母部分,然后單獨檢查后面的數字。

如果你想要它容易但很慢 - 去RegExp方式。

如果您想要快速,請閱讀@LeleDumbo的回答。

但! 在真正的搜索之前使字符串全部大寫 - AnsiUpperCase函數。 不區分大小寫的搜索會減慢每個字符的速度。 最好是復制字符串和搜索模式。 (哦,@ RobMcDonell已告訴你:-))

您要將前綴轉換為樹。 好的,在這個簡單的例子中它將適合列表(數組):“V”,“OL”,“UME” 在更復雜的情況下你可以用相同的開始搜索V-OL-UME或V-ER-SION和分裂的尾巴)

然后閱讀http://en.wikipedia.org/wiki/Finite-state_machine - 這就是你必須要做的事情。

一個簡單的草案(不涵蓋所有可能的用例,例如“Vol.2.2”)將是:

從search-txt-1狀態開始,#1 char來查看。 在每個循環中,您有當前狀態和當前要考慮的字符數(想到左邊已經掃描過的所有字符):

  1. 如果state是search-txt-1,則在當前字符和右邊任意位置搜索txt-1(即“V”)(System.StrUtils.PosEx函數)

    1.1。 如果未找到 - 退出循環,則找不到文本

    1.2。 如果找到 - inc(當前數字),則:= search-txt-2,下一個循環

  2. 如果state是search-txt-2,則僅搜索當前字符的txt-2(“UM”)! (懶惰:System.Copy(txt,current-char,system.length(txt-2))= txt-2; fast:與Jedi CodeLibrary的長度和偏移量的特殊比較)

    2.1如果找到,inc(當前數字,長度(txt-2),狀態:= search-txt-3,下一循環

    2.2如果沒有找到,請不要更改當前編號,狀態:= skip-dot,next loop

  3. 如果state是search-txt-3,那么就像上面一樣搜索txt-3

    3.1 if found,inc(current-number,length(txt-3),state:= skip-dot,next loop

    3.2如果沒有找到,請不要更改當前編號,狀態:=跳過點,下一個循環

  4. 如果state是skip-dot,看看current-char是否為dot

    4.1如果是,inc(current-number),state:= skip-few-blanks,next loop

    4.2如果不是不改變當前數字,則表示:= skip-few-blanks,next loop

  5. 如果skip-few-blanks則查看current-char是否為“”

    5.1如果是,inc(current-number),state:= skip-few-blanks,next loop(可能有更多空格)

    5.2如果不是不改變當前數字,則:= maybe-number,next loop

  6. if maybe-number然后是System.Character.IsDigit(current-char)???

    6.1如果不是 - 沒有數字,搜索失敗,下次嘗試 - 不要改變當前號碼,狀態:= search-txt-1,下一個循環

    6.2如果是,記住數字開始的地方,狀態:= reading-number,inc(當前數字),下一個循環

  7. 如果讀取數字然后System.Character.IsDigit(current-char)???

    7.1如果是 - 再多一位 - 狀態:=讀數,inc(當前數),下一循環

    7.2如果不是 - 數字超過 - 從數字開始到前一個字符(最后一個數字)獲取字符串片段,轉換它(IntToStr(復制(字符串,數字 - 開始,數字長度))並退出循環(你不在一個字符串中搜索幾個數字,對嗎?)

對於更復雜的語法,有像Yacc / Bison這樣的工具。 但是對於這么簡單的你可以選擇你自己的自定義FSM,它並不難,但最快的方式。 只是要非常注意,不要在狀態轉換和當前字符數字轉換中出錯。

我希望我沒有,但你必須測試它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM