如何在字符串中搜索子字符串的许多变体

时间:2012-07-25 21:13:26

标签: delphi delphi-xe2

我正在尝试在一个字符串中搜索一个子字符串,但是必须要有一个更有效的方法呢...

      //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';

7 个答案:

答案 0 :(得分:11)

由于您使用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;

现在,我不是最擅长设计正则表达式,但我测试了上面的那个,它似乎与你所有的变化相匹配(也许其他人可以想出一个更简洁的表达式)。

答案 1 :(得分:5)

对于很多字符串和频繁搜索,使用后缀树将是您最好的选择。否则,使用正则表达式的更简单方法也可以提供帮助,您的字符串看起来足够规则。

答案 2 :(得分:5)

基于@ user582118的回答:

如果使用^v(ol\.?|olume)?\s*([0-9]+)$作为RegEx模式,则不必尝试每个可能的数值。它将与最后的一个或多个数字字符匹配。然后,您可以使用TMatch的{​​{1}}和Value属性从字符串中提取数字。

Groups

给出:

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;

答案 3 :(得分:4)

尝试这样的事情:

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;

答案 4 :(得分:3)

我必须做一次类似的事情来比较邮寄地址。我删除了空格和标点符号。然后我使用了CompareText,因此它不区分大小写。

很多你的If语句涉及比较可能有或没有“Vol”或“Volume”之间的句号或空格的字符串和数字。删除句点和空格,每个卷号留下两个If语句:一个用于VOL,一个用于VOLUME。您甚至可以通过将“volume”替换为“vol”来将每个音量降低到一个If语句。

答案 5 :(得分:2)

首先将搜索字符串设置为大写(一次),然后针对搜索字符串的大写版本执行每项检查。这样可以将检查次数减少一半而不需要不区分大小写的搜索(这可能会每次更改两个字符串的大小写)。

您可以更进一步,使用JCL中的一个通配符匹配函数,例如StrMatches。但是,虽然这会减少代码行数,但它不能像具有特定匹配项一样快。

如果您希望为Volume创建许多不同的值,请编写自己的函数来搜索字符串的字母部分,然后单独检查后面的数字。

答案 6 :(得分:2)

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

如果您想要快速,请阅读@LeleDumbo的回答。

BUT! 在真正的搜索之前使字符串全部大写 - 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。 if found - inc(current-number),state:= search-txt-2,next loop

  2. 如果state是search-txt-2,则仅搜索当前字符的txt-2(“UM”)! (懒惰:System.Copy(txt,current-char,system.length(txt-2))= txt-2; fast:与Jedi CodeLibrary的长度和偏移量进行特殊比较)

    2.1 if found,inc(current-number,length(txt-2),state:= search-txt-3,next loop

    2.2如果没有找到,请不要更改当前号码,状态:=跳过点,下一个循环

  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如果是 - 再多一位 - 状态:= reading-number,inc(当前数字),下一循环

    7.2如果不是 - 数字超过 - 从数字开始到前一个字符(最后一个数字)获取字符串片段,转换它(IntToStr(复制(字符串,数字开始,数字长度))并退出循环(你没有在一个字符串中搜索多个数字,对吗?)

  8. 对于更复杂的语法,有像Yacc / Bison这样的工具。 但是对于这么简单的你可以选择你自己的自定义FSM,它并不难,但最快的方式。只是要非常注意,不要在状态转换和当前字符数字转换中出错。

    我希望我没有做,但你必须测试它。