-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathWDDT.StringTools.pas
More file actions
185 lines (159 loc) · 4.78 KB
/
WDDT.StringTools.pas
File metadata and controls
185 lines (159 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
unit WDDT.StringTools;
interface
uses
System.SysUtils;
// The following functions Empty and Defined are the better performing variants for the
// frequently used string comparison "Trim(X) = ''" (Empty) or "Trim(X) <> ''" (Defined)
// Depending on the input string the functions are faster by a factor of 5-100. The acceleration
// results from the short evaluation which is performed directly on the input string without a
// temporary generated string and by saving a function jump (no Trim). Also is the memory manager
// not involved.
function Empty(const s: string): Boolean;
function Defined(const s: string): Boolean;
function IfEmpty(const Source, ReplaceWith: string): string;
function CompareStringNatural(const StringA, StringB: string): Integer;
function RemoveMultipleSpaces(const Value: string): string;
implementation
// Returns True if no single visible character is contained, otherwise False
function Empty(const s: string): Boolean;
var
cc: Integer;
begin
cc := Length(s);
while (cc > 0) and (s[cc] <= ' ') do
Dec(cc);
Result := cc = 0;
end;
// Returns True if any visible character is contained in the passed string, otherwise False
//
// Is the opposite of the Empty function.
function Defined(const s: string): Boolean;
var
cc: Integer;
begin
cc := Length(s);
while (cc > 0) and (s[cc] <= ' ') do
Dec(cc);
Result := cc > 0;
end;
// If the param Source is empty (see function Empty), the result is the second parameter
// ReplaceWith, otherwise Source is returned.
function IfEmpty(const Source, ReplaceWith: string): string;
begin
if Empty(Source) then
Result := ReplaceWith
else
Result := Source;
end;
// Compare function for "Natural sort order"
//
// https://en.wikipedia.org/wiki/Natural_sort_order
//
// Based on https://github.com/slackydev/SimbaExt/blob/37416bbbc16343a5789c18857450848b5057d297/DLL%20Source/SimbaExt/src/sorting/TSASort.pas
function CompareStringNatural(const StringA, StringB: string): Integer;
type
TNaturalString = record
Text: string;
Number: Integer;
IsText: Boolean;
end;
TNaturalArray = array of TNaturalString;
function IsDigit(AChar: Char): Boolean; inline;
begin
Result := CharInSet(AChar, ['0'..'9']);
end;
// Splits strings and numbers in to a TNaturalArray, outputing something like: ['asd',123,'cat'].
function GetNaturalString(Str: string): TNaturalArray;
var
i, L, j: Int32;
IsText: Boolean;
Temp: String;
begin
Result := nil;
Temp := '';
L := Length(Str);
j := 0;
for i := 1 to L do
begin
IsText := not IsDigit(Str[i]);
if IsText then
begin
Temp := Temp + Str[i];
// Continue, when the next char is also *not* a digit
if (i + 1 <= L) and not IsDigit(Str[i+1]) then
Continue;
end
else
begin
Temp := Temp + Str[i];
// Is the next char also a digit...then continue
if (i + 1 <= L) and IsDigit(Str[i + 1]) then
Continue;
end;
if Defined(Temp) then
begin
SetLength(Result, j + 1);
Result[j].IsText := IsText;
if IsText then
Result[j].Text := Temp
else
Result[j].Number := StrToInt(Temp);
Inc(j);
Temp := '';
end;
end;
end;
var
cc: Integer;
ListA, ListB: TNaturalArray;
LengthA, LengthB, LengthMin: Integer;
begin
ListA := GetNaturalString(StringA);
ListB := GetNaturalString(StringB);
LengthA := Length(ListA);
LengthB := Length(ListB);
if LengthA < LengthB then
LengthMin := LengthA
else
LengthMin := LengthB;
Result := 0;
for cc := 0 to LengthMin - 1 do
begin
if (not ListA[cc].IsText) and (not ListB[cc].IsText) then
Result := ListA[cc].Number - ListB[cc].Number
else if ListA[cc].IsText and ListB[cc].IsText then
Result := CompareText(ListA[cc].Text, ListB[cc].Text)
else if (not ListA[cc].IsText) and ListB[cc].IsText then
Result := -1
else if ListA[cc].IsText and (not ListB[cc].IsText) then
Result := 1
else
Result := 0;
if Result <> 0 then
Exit(Result);
end;
if Result = 0 then
Result := LengthA - LengthB;
end;
// Replaces multiple spaces in a row with a single one
function RemoveMultipleSpaces(const Value: string): string;
var
LastIsSpace: Boolean;
I, Cnt: Integer;
Res: string;
begin
SetLength(Res, Length(Value));
Cnt := 0;
LastIsSpace := False;
for I := 1 to Length(Value) do
begin
if LastIsSpace and (Value[I] = #32) then
Continue;
LastIsSpace := Value[I] = #32;
Inc(Cnt);
Res[Cnt] := Value[I];
end;
SetLength(Res, Cnt);
Result := Res;
end;
end.