Set Of String??!!
You are familiar with this block:
Var
mySet: Set Of Char;
C:开发者_运维知识库 Char;
begin
mySet := ['a', 'b', 'c'];
If C In mySet Then ShowMessage('Exists');
end;
Is there any way to declare Set Of STRING
? or is there similar code that i can use instead? The important part of this block is If C In mySet Then ShowMessage('Exists');
I wanna use something like this about a set of string.
Sets are implemented using bit arrays. So no, you cannot have a 'set of string'. Use a TStringList instead, ie:
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;
The RTL System.StrUtils
unit provides a very interesting method for this:
function MatchText(const AText: string; const AValues: array of string): Boolean; overload;
Use it like this:
if MatchText(sLanguages, ['fr-FR', 'en-GB', 'de-DE', 'it-IT', 'fr-CH', 'es-ES']) then
Writeln('found')
You can make use of this.
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;
It is shame, but Delphi still don't have even very basic data structures, like sets. Suggestion of "Fabricio Araujo" is very limited in usage - you have to know all possible strings, so it is useless when you read them from somewhere. It is also impossible to have more then 256 items in such "alsmost set of strings". Remy Lebeau suggested to use TStringList for that purpose, but it is absolutely inefficient when you have big sets. In modern versions of Delphi it is possible to use TDictionary to keep unordered set of anything, including strings:
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;
It is case sensitive. If you need case insensitive implementation, you can make all strings lowercased before put/look then in set (use AnsiLowerCase function for that).
Fabricio Araujo posted the best answer to the original question.
But if you are asking that question you should also ask yourself
"Do I only need to know if Str is in this Set?".
For the question "Is MonthAbbr in the set of allowed abbreviations of month names?"
chances are your next question is "Well then, which month is it?".
- You code should answer both questions in one function call. (e.g. return Month Number else 0)
- Should be Readable. Make your intent imediatly clear to the code maintainer.
- Should store the allowed strings in a single obvious place in code.
- Should allow easy revision ("Add 'Fall' and 'Autumn' and bring that back to me in 15 min.").
- Be reasonably efficient for the expected size of the set.
- Use the standard library (VCL) if possible. Else use an external library.
- If your code does this sort of thing often, separate the strings from the implementation, and put the implementation in one place so it can be changed cleanly.
So your code might read
case GetMonthNumber(Str) of
0: //Error: Str is not a Month Name or Abbreviation
1: //etc.
For the implementation, the simplest general answer is to use a sorted TStringList in Delphi 7 (as the question is tagged).
Another option is to declare a new "String" type and use a RECORD HELPER to implement the "IN" operator on it:
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.
It is implicitly compatible with STRING type in assignments and comparisons. It is implicitly case insensitive in the "IN" operator, and by extending the record helper (with Equal/NotEqual/GreaterThan/LessThan/GreaterThanOrEqual/LessThanOrEqual operators to STRING and TextString types) you can make a fully-case-insensitive string type.
Disadvantage: You cannot use this new type in VAR/OUT STRING parameters, and it's a little bit slower than a standard STRING type due to its conversion back and forth and UPPER-casing in comparisons.
function MatchStr(const AText: string; const AValues: array of string): Boolean; overload;
MatchStr determines if any of the strings in the array AValues match the string specified by AText using a case sensitive comparison. It returns true if at least one of the strings in the array match, or false if none of the strings match.
For a case insensitive match, use the MatchText routine.
精彩评论