【问题标题】:Set Of String??!!字符串集??!​​!
【发布时间】:2010-06-30 15:37:44
【问题描述】:

你对这个块很熟悉:

Var
  mySet: Set Of Char;
  C: Char;
begin
  mySet := ['a', 'b', 'c'];
  If C In mySet Then ShowMessage('Exists');
end;

有没有办法声明Set Of STRING?还是我可以使用类似的代码?这个块的重要部分是If C In mySet Then ShowMessage('Exists'); 我想对一组字符串使用类似的东西。
谢谢。

【问题讨论】:

    标签: delphi delphi-7


    【解决方案1】:

    集合是使用位数组实现的。所以不,你不能有一个“字符串集”。改用 TStringList,即:

    var 
      mySet: TStringList;
      S: String;
    begin 
      S := ...;
      mySet := TStringList.Create;
      try
        mySet.Add('a');
        mySet.Add('b');
        mySet.Add('c'); 
        if mySet.IndexOf(S) <> -1 Then ShowMessage('Exists');
      finally
        mySet.Free;
      end;
    end; 
    

    【讨论】:

    • 如果您有很多字符串,最好使用排序列表并忽略重复项:mySet.Sorted := True; mySet.Duplicates := dupIgnores;
    【解决方案2】:

    RTL System.StrUtils 单元为此提供了一个非常有趣的方法:

    function MatchText(const AText: string; const AValues: array of string): Boolean; overload;
    

    像这样使用它:

      if MatchText(sLanguages, ['fr-FR', 'en-GB', 'de-DE', 'it-IT', 'fr-CH', 'es-ES']) then
        Writeln('found')
    

    【讨论】:

    • 这会在编译时创建一个 [0..5] 的字符串数组并将其传递给函数。优点:它是 1 行。在非常有限的情况下这很好。例如。该字段中是否允许使用此字符串?缺点:如果您使用该 1-liner 两次,您就会引发错误。您的代码的维护者可能会在一个实例中更改该设置而错过另一个。此外,RTL 使用字符串比较对该数组执行低效的线性搜索。 Pos(sLanguages,'fr-FR,en-GB,de-DE,it-IT,fr-CH,es-ES')>0 会更快——但仍然不是一个好主意。
    • @GuyGordon 1. MatchText 函数定义如下: function MatchText(const AText: string; const AValues: array of string): Boolean; -> 数组参数可以引用 const 引用,例如 "const cStrings: array of string = ['fr-FR', 'en-GB', 'de-DE', 'it-IT', 'fr-CH ','es-ES']”。 -> 数组现在只在一个地方引用,即你想要的。 2.您使用“Pos”的建议给出了错误的结果,因为它错误地接受了“fr”或“GB”之类的字符串。
    • 糟糕。你是对的,谢谢。您必须在子字符串和搜索字符串周围放置分隔符。而且它区分大小写。就像我说的,这不是一个好主意。
    【解决方案3】:

    你可以利用这个。

    type 
      TAnyEnum = (aeVal1, aeVal2, aeVal3);
      TEnuns = set of TAnyEnum;
      TAnyMessages: array [TAnyEnum] of String;
    
    const 
      MyMessages: TAnyMessages = ('Exists', 'Something else', 'WTF!?');
    
    var
      MySet : TEnums;
    begin
      MySet = [aeVal1, aeVal2];
      If aeVal1 in MySet then ShowMessage(MyMessages[aeVal1]);
    end;
    

    【讨论】:

    • 要正确回答问题,入口点应该是字符串,而不是枚举类型。 -> 这个问题没有得到尊重
    • @DidierCabalé 但是 Delphi 集 必须 基于 ORDINAL 类型。字符串不是序数类型。离你越近的一组字符串就是那个。
    • @FabricioAraujo,这取决于您关注的地方:如果是使用 Set of,我同意这是最接近的。但这并不能回答 如何知道特定字符串是否在一组字符串中这一基本问题,在这种情况下,您不能使用 Pascal 构造“set of”
    【解决方案4】:

    很遗憾,但 Delphi 仍然没有非常基本的数据结构,比如集合。 “Fabricio Araujo”的建议在使用上非常有限——你必须知道所有可能的字符串,所以当你从某个地方读取它们时它是没有用的。在这样的“几乎一组字符串”中也不可能有超过 256 个项目。 Remy Lebeau 建议为此目的使用 TStringList,但是当你有大集合时它绝对是低效的。在现代版本的 Delphi 中,可以使用 TDictionary 来保持任何东西的无序集,包括字符串:

    procedure TForm6.FormCreate(Sender: TObject);
    type
      TEmpty = record end;
    var
      MySet: TDictionary<String, TEmpty>;
      Dummy: TEmpty;
    begin
      MySet := TDictionary<String, TEmpty>.Create;
      try
        MySet.Add('Str1', Dummy);
        MySet.Add('Str2', Dummy);
        MySet.Add('Str3', Dummy);
        if MySet.TryGetValue('Str2', Dummy) then
          ShowMessage('Exists');;
      finally
        MySet.Free;
      end;
    end;
    

    区分大小写。如果您需要不区分大小写的实现,您可以在 put/look 之前将所有字符串小写,然后在 set 中(使用 AnsiLowerCase 函数)。

    【讨论】:

    • 我在 .NET 中使用字典的大部分时间是在我的解决方案足够的情况下。
    • “所以当你从某个地方阅读它们时它是无用的”。不,不是没用。不要使用数组常量,而是使用数组变量并从你想要的源中填充它。
    【解决方案5】:

    Fabricio Araujo 发布了对原始问题的最佳答案。
    但是,如果您要问这个问题,您也应该问自己
    “我是否需要知道 Str 是否在此 Set 中?”。

    对于“MonthAbbr 是否在允许的月份名称缩写集中?”这个问题
    您的下一个问题很可能是“那么,是哪一个月?”。

    • 您的代码应该在一个函数调用中回答这两个问题。 (例如返回月份编号,否则返回 0)
    • 应该是可读的。让代码维护人员立即了解您的意图
    • 应将允许的字符串存储在代码中的单个明显位置。
    • 应该允许轻松修改(“添加 'Fall' 和 'Autumn' 并在 15 分钟内将其返回给我”)。
    • 对于集合的预期大小要合理有效。
    • 尽可能使用标准库 (VCL)。否则使用外部库。
    • 如果您的代码经常执行此类操作,请将字符串与实现分开,并将实现放在一个位置,以便可以干净利落地更改。

    因此您的代码可能会显示为
    case GetMonthNumber(Str) of 0: //Error: Str is not a Month Name or Abbreviation 1: //etc.

    对于实现,最简单的通用答案是在 Delphi 7 中使用排序的 TStringList(因为已标记问题)。

    【讨论】:

      【解决方案6】:

      另一种选择是声明一个新的“String”类型并使用 RECORD HELPER 在其上实现“IN”运算符:

      USES System.SysUtils, System.StrUtils;
      
      TYPE
        TextString    = RECORD
                        PRIVATE
                          STR                 : STRING;
                        PUBLIC
                          CLASS OPERATOR      Implicit(CONST S : STRING) : TextString; INLINE;
                          CLASS OPERATOR      Implicit(CONST S : TextString) : STRING; INLINE;
                        END;
      { TextString }
      
      CLASS OPERATOR TextString.Implicit(CONST S : STRING) : TextString;
        BEGIN
          Result.STR:=S
        END;
      
      CLASS OPERATOR TextString.Implicit(CONST S : TextString) : STRING;
        BEGIN
          Result:=S.STR
        END;
      
      TYPE
        TStringHelper = RECORD HELPER FOR TextString
                          CLASS OPERATOR In(CONST T : TextString ; CONST ARR : ARRAY OF STRING) : BOOLEAN;
                          FUNCTION       IsEmpty : BOOLEAN;
                        END;
      
      { TStringHelper }
      
      CLASS OPERATOR TStringHelper.In(CONST T : TextString ; CONST ARR : ARRAY OF STRING) : BOOLEAN;
        BEGIN
          Result:=MatchText(T.STR,ARR)
        END;
      
      FUNCTION TStringHelper.IsEmpty : BOOLEAN;
        BEGIN
          Result:=STR.IsEmpty
        END;
      
      VAR
        S     : TextString;
        T     : STRING;
      
      begin
        try
          REPEAT
            WRITE('Enter an animal name: '); READLN(T); S:=T;
            IF S.IsEmpty THEN EXIT;
            IF S IN ['Bird','Lion','Monkey'] THEN WRITELN('You got it!') ELSE WRITELN('Nope - Try Again...')
          UNTIL FALSE
        except
          on E: Exception do
            Writeln(E.ClassName, ': ', E.Message);
        end;
      end.
      

      它在赋值和比较中隐式兼容 STRING 类型。它在“IN”运算符中隐式不区分大小写,并且通过扩展记录助手(使用 Equal/NotEqual/GreaterThan/LessThan/GreaterThanOrEqual/LessThanOrEqual 运算符到 STRING 和 TextString 类型),您可以创建完全不区分大小写的字符串类型。

      缺点:你不能在 VAR/OUT STRING 参数中使用这种新类型,并且由于它的来回转换和比较中的大写,它比标准 STRING 类型慢一点。

      【讨论】:

        【解决方案7】:

        function MatchStr(const AText: string; const AValues: array of string): Boolean;超载;

        MatchStr 使用区分大小写比较来确定数组 AValues 中的任何字符串是否与 AText 指定的字符串匹配。如果数组中至少有一个字符串匹配,则返回 true,如果没有任何字符串匹配,则返回 false。

        对于不区分大小写的匹配,请使用 MatchText 例程。

        【讨论】:

          猜你喜欢
          • 2011-02-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-10-15
          • 2010-11-28
          • 1970-01-01
          相关资源
          最近更新 更多