{ *************************************************************************** } { } { EControl Common Library } { } { Copyright (c) 2004 - 2015 EControl Ltd., Zaharov Michael } { www.econtrol.ru } { support@econtrol.ru } { } { *************************************************************************** } {$mode delphi} unit ecStrUtils; interface uses SysUtils, Classes, Graphics; type ecString = UnicodeString; ecChar = WideChar; UCString = UnicodeString; UCChar = WideChar; type TzStringList = class(TStringList) private FDelimiter: Char; FCaseSensitive: Boolean; function GetDelimitedText: string; procedure SetDelimitedText(const Value: string); procedure SetCaseSensitive(const Value: Boolean); public function Find(const S: string; out Index: Integer): Boolean; override; procedure Sort; override; property Delimiter: Char read FDelimiter write FDelimiter; property DelimitedText: string read GetDelimitedText write SetDelimitedText; property CaseSensitive: Boolean read FCaseSensitive write SetCaseSensitive; private function GetValueFromIndex(Index: Integer): string; procedure SetValueFromIndex(Index: Integer; const Value: string); public property ValueFromIndex[Index: Integer]: string read GetValueFromIndex write SetValueFromIndex; end; function IsDigitChar(const c: UCChar): Boolean; overload; function IsHexDigitChar(const c: UCChar): Boolean; overload; function IsLineBreakChar(const c: UCChar): Boolean; overload; function IsWordChar(const c: UCChar): Boolean; overload; function IsSpaceChar(const c: UCChar): Boolean; overload; function IsAlphaChar(const c: UCChar): Boolean; overload; function IsIdentChar(const C: UCChar): Boolean; overload; function IsIdentDigitChar(const C: UCChar): Boolean; overload; function IsIdentLetterChar(const C: UCChar): Boolean; overload; function IsWordBreak(aPos: integer; const Text: UCString): Boolean; overload; function ecUpCase(const C: UCChar): UCChar; overload; function SkipSpaces(const Source: ecString; var APos: integer): integer; function SkipSpacesNoLineBreak(const Source: ecString; var APos: integer): integer; function ecEncodeString(const S: string): string; function ecDecodeString(const S: string): string; function ecPosEx(const SubStr, S: ecString; Offset: Cardinal = 1): Integer; implementation uses Controls, Forms; function TzStringList.GetDelimitedText: string; var S: string; P: PChar; I, NCount: Integer; //renamed Count begin NCount := GetCount; if (NCount = 1) and (Get(0) = '') then Result := QuoteChar + QuoteChar else begin Result := ''; for I := 0 to NCount - 1 do begin S := Get(I); P := PChar(S); while not (P^ in [#0..' ', QuoteChar, Delimiter]) do Inc(P); if (P^ <> #0) then S := AnsiQuotedStr(S, QuoteChar); Result := Result + S + Delimiter; end; System.Delete(Result, Length(Result), 1); end; end; procedure TzStringList.SetCaseSensitive(const Value: Boolean); begin if Value <> FCaseSensitive then begin FCaseSensitive := Value; if Sorted then Sort; end; end; procedure TzStringList.SetDelimitedText(const Value: string); var P, P1: PChar; S: string; begin BeginUpdate; try Clear; P := PChar(Value); while P^ in [#1..' '] do Inc(P); while P^ <> #0 do begin P1 := P; while (P^ > ' ') and (P^ <> Delimiter) do Inc(P); SetString(S, P1, P - P1); Add(S); while P^ in [#1..' '] do Inc(P); if P^ = Delimiter then begin P1 := P; Inc(P1); if P1^ = #0 then Add(''); repeat Inc(P); until not (P^ in [#1..' ']); end; end; finally EndUpdate; end; end; function StrAnsiCompare(List: TStringList; const S1, S2: string): Integer; begin with TzStringList(List) do if CaseSensitive then Result := AnsiCompareStr(S1, S2) else Result := AnsiCompareText(S1, S2); end; function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer; begin Result := StrAnsiCompare(List, List[Index1], List[Index2]); end; function TzStringList.Find(const S: string; out Index: Integer): Boolean; var L, H, I, C: Integer; begin Result := False; L := 0; H := Count - 1; while L <= H do begin I := (L + H) shr 1; C := StrAnsiCompare(Self, Strings[I], S); if C < 0 then L := I + 1 else begin H := I - 1; if C = 0 then begin Result := True; if Duplicates <> dupAccept then L := I; end; end; end; Index := L; end; procedure TzStringList.Sort; begin CustomSort(StringListAnsiCompare); end; function TzStringList.GetValueFromIndex(Index: Integer): string; begin if Index >= 0 then Result := Copy(Get(Index), Length(Names[Index]) + 2, MaxInt) else Result := ''; end; procedure TzStringList.SetValueFromIndex(Index: Integer; const Value: string); begin if Value <> '' then begin if Index < 0 then Index := Add(''); Put(Index, Names[Index] + '=' + Value); end else if Index >= 0 then Delete(Index); end; //============================================================================== // Routines //============================================================================== function IsSpaceChar(const c: UCChar): Boolean; begin Result := c = ' '; end; function IsLineBreakChar(const c: UCChar): Boolean; begin case C of #$000A, #$000D, #$2028, #$2029, #$0085: Result := True; else Result := False; end; end; function IsDigitChar(const C: UCChar): Boolean; begin Result := Pos(c, '1234567890') > 0; end; function IsHexDigitChar(const C: UCChar): Boolean; begin Result := Pos(c, '1234567890abcdefABCDEF') > 0; end; function IsWordChar(const C: UCChar): Boolean; begin if IsDigitChar(C) then Result := True else if (C >= 'a') and (C <= 'z') then Result := True else if (C >= 'A') and (C <= 'Z') then Result := True else if (C = '_') or (C = #$0301) //AT or (C = #$00B4) //AT or (C = #$02B9) //AT or (C = #$02CA) //AT or (C = #$0384) then Result := True else Result := False; end; function IsAlphaChar(const C: UCChar): Boolean; begin Result := ((C >= 'a') and (C <= 'z')) or ((C >= 'A') and (C <= 'Z')); end; function IsIdentChar(const C: UCChar): Boolean; begin Result := IsIdentLetterChar(C) or IsIdentDigitChar(C); end; function IsIdentLetterChar(const C: UCChar): Boolean; begin Result := ((C >= 'a') and (C <= 'z')) or ((C >= 'A') and (C <= 'Z')) or (C = '_'); end; function IsIdentDigitChar(const C: UCChar): Boolean; begin Result := (C >= '0') and (C <= '9'); end; function IsWordBreak(aPos: integer; const Text: UCString): Boolean; begin Result := (aPos = 1) or (aPos > Length(Text)) or (IsWordChar(Text[aPos]) xor IsWordChar(Text[aPos - 1])); end; function ecUpCase(const C: UCChar): UCChar; begin Result := UpCase(C); end; function SkipSpacesNoLineBreak(const Source: ecString; var APos: integer): integer; var N: integer; begin Result := 0; N := Length(Source); while (APos <= N) and IsSpaceChar(Source[APos]) and not IsLineBreakChar(Source[APos]) do inc(APos); if APos > N then Result := -1; end; function SkipSpaces(const Source: ecString; var APos: integer): integer; var N: integer; begin Result := 0; N := Length(Source); while (APos <= N) and IsSpaceChar(Source[APos]) do begin if Source[APos] = #10 then inc(Result); inc(APos); end; if APos > N then Result := -1; end; function ecEncodeString(const S: string): string; var I, L, K: integer; begin Result := ''; L := Length(S); I := 1; while I <= L do if (S[I] >= ' ') and (S[I] <> '''') then begin K := I; repeat Inc(I) until (I > L) or (S[I] < ' ') or (S[I] = ''''); Result := Result + '''' + Copy(S, K, I - K) + ''''; end else begin Result := Result + '#' + IntToStr(Ord(S[I])); Inc(I); end; end; function ecDecodeString(const S: string): string; var I, L, K: integer; begin Result := ''; L := Length(S); I := 1; while I <= L do if S[I] = '''' then begin K := I; repeat Inc(I); until (I > L) or (S[i] = ''''); Result := Result + Copy(S, K + 1, I - K - 1); Inc(I); end else if S[I] = '#' then begin K := I + 1; repeat Inc(I) until (I > L) or not IsIdentDigitChar(S[I]); if (K = I) or ((I - K) > 3) then raise Exception.Create('Invalid character code'); Result := Result + Chr(StrToInt(Copy(S, K, I - K))); end else Exit; // else raise Exception.Create('Invalid property data'); end; function ecPosEx(const SubStr, S: ecString; Offset: Cardinal = 1): Integer; var I,X: Integer; Len, LenSubStr: Integer; begin if Offset = 1 then Result := Pos(SubStr, S) else begin I := Offset; LenSubStr := Length(SubStr); Len := Length(S) - LenSubStr + 1; while I <= Len do begin if S[I] = SubStr[1] then begin X := 1; while (X < LenSubStr) and (S[I + X] = SubStr[X + 1]) do Inc(X); if (X = LenSubStr) then begin Result := I; exit; end; end; Inc(I); end; Result := 0; end; end; function ChangeComponentReference(This, NewRef: TComponent; var RefVar: TComponent): Boolean; begin Result := (RefVar <> NewRef) and Assigned(This); if Result then begin if Assigned(RefVar) then RefVar.RemoveFreeNotification(This); RefVar := NewRef; if Assigned(RefVar) then RefVar.FreeNotification(This); end; end; end.