Bonjour à tous !
Est-il possible de spliter une chaine dans un tableau à partir d'un délimiteur ?
Merci à tous,
@+
Bonjour à tous !
Est-il possible de spliter une chaine dans un tableau à partir d'un délimiteur ?
Merci à tous,
@+
Vous trouverez dans la FAQ, les sources ou les tutoriels, de l'information accessible au plus grand nombre, plein de bonnes choses à consulter sans modération![]()
Des tutoriels pour apprendre à créer des formulaires de planning dans vos applications Access :
Gestion sur un planning des présences et des absences des employés
Gestion des rendez-vous sur un calendrier mensuel
Importer un fichier JSON dans une base de données Access :
Import Fichier JSON
Salut,
Oui la fonction recherche du forum est la pour ca
Recherche Explode ou Split et je pense que tu devrais trouver ton bonheur
DragonHeart.
ExtractStrings de Delphi (Classes.pas)
Sinon, ICI, tu as un message de moi, qui énumère toutes les variantes disponibles de Explode/Split, et leur performance (à noter que mes fonctions sont basés sur un précalcul de la longueur du tableau sortant, ce qui dans certains cas, peu être plus lent, mais dans une grande majorité, cela dépote un max)
les Dernières versions étant celles-ci (sur le forum peu y a voir plus de 10 versions buggées ...), donc voici le code :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 {* ----------------------------------------------------------------------------- la fonction ExplodeLazy retourne un tableau de chaînes. Ce sont les sous-chaînes, extraites de S, en utilisant le séparateur Separator. @param S Chaine à découper @param A Tableau de Chaine qui recevra la découpe @param Separator Caractère qui délimitent une chaine pour la découpe @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !) ------------------------------------------------------------------------------ } function ExplodeLazy(const S: string; out A: Types.TStringDynArray; Separator: Char): Integer; var I, J, K: integer; iLenS: integer; begin iLenS := Length(S); if iLenS = 0 then begin SetLength(A, 1); Result := 0; A[Result] := ''; Exit; end; Result := 0; for I := 1 to iLenS do if S[I] = Separator then Inc(Result); if S[iLenS] = Separator then SetLength(A, Result) else SetLength(A, Result + 1); K := 1; J := 0; for I := 1 to iLenS do if S[I] = Separator then begin if K <> I then A[J] := Copy(S, K, I - K); Inc(J); K := I + 1; end; if K <= iLenS then A[J] := Copy(S, K, MaxInt); end;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 {* ----------------------------------------------------------------------------- la fonction ExplodeLazyToStrings encapsule ExplodeLazy pour renvoyer une liste. @param S Chaine à découper @param A Tableau de Chaine qui recevra la découpe @param Separator Caractère qui délimitent une chaine pour la découpe @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !) ------------------------------------------------------------------------------ } function ExplodeLazyToStrings(const S: string; L: TStrings; Separator: Char; DoClear: Boolean = True): Integer; var A: Types.TStringDynArray; iL: Integer; begin if Assigned(L) then begin Result := Explode(S, A, Separator); if DoClear then begin L.Clear(); L.Capacity := Result; end else L.Capacity := L.Capacity + Result; for iL := Low(A) to High(A) do L.Add(A[iL]); end else Result := -1; end;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
186
187
188
189
190 {* ----------------------------------------------------------------------------- la fonction Explode retourne un tableau de chaînes. Ce sont les sous-chaînes, extraites de S, en utilisant le séparateur Separator. cela peut servir pour lire du CSV @param S Chaine à découper @param A Tableau de Chaine qui recevra la découpe @param Separators Caractères qui délimitent une chaine pour la découpe @param ExcludeEmpty Si True, les Chaines vides ne sont pas insérés dans le Tableau @param Quotes Caractères qui délimitent une chaine pour la découpe contenant des Separators, n'importe quel séparateur peut commencer et terminé une chaine, une quote doublée est considéré comme valeur un quote dans la chaine @param KeepSeparators Si True, A contient les chaines et les séparateurs mais pas les Quotes, sinon (par défaut) A ne contient que les Chaines. @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !) ------------------------------------------------------------------------------ } function Explode(const S: string; out A: Types.TStringDynArray; const Separators: string; ExcludeEmpty: Boolean = False; const Quotes: string = ''; KeepSeparators: Boolean = False): Integer; var iLesSep: Integer; iLesQuote: Integer; function IsSeparator(C: Char): Integer; begin for Result := 1 to iLesSep do if C = Separators[Result] then Exit; Result := -1; end; function IsQuote(C: Char): Integer; begin for Result := 1 to iLesQuote do if C = Quotes[Result] then Exit; Result := -1; end; var iStr: Integer; iQuote: Integer; iLenS: Integer; iLenSS: Integer; iLenA: Integer; iAdded: Integer; iBegin: Integer; Quoted: Boolean; DoubleQuoted: Boolean; AlreadyDQ: Boolean; QuoteConcat: string; iOffQuote: Integer; LastIsSep: Boolean; begin iLenS := Length(S); iLesSep := Length(Separators); if (iLenS = 0) or (iLesSep = 0) then begin SetLength(A, 1); Result := 0; A[Result] := ''; Exit; end; iLesQuote := Length(Quotes); for iQuote := 1 to iLesQuote do if IsSeparator(Quotes[iQuote]) > 0 then raise EParserError.CreateFmt('le Délimiteur "%s" ne peut pas être un Séparateur !', [Quotes[iQuote]]); Result := 0; iQuote := 0; for iStr := 1 to Length(S) do begin if IsSeparator(S[iStr]) > 0 then Inc(Result) else if IsQuote(S[iStr]) > 0 then Inc(iQuote); end; if Odd(iQuote) then raise EParserError.CreateFmt('Nombre de Délimiteur Incorrect : "%d" !', [iQuote]); LastIsSep := IsSeparator(S[iLenS]) > 0; if KeepSeparators then iLenA := Result * 2 + 1 else iLenA := Result + 1; SetLength(A, iLenA); iLenSS := 0; iAdded := 0; Quoted := False; iOffQuote := 0; QuoteConcat := ''; AlreadyDQ := False; iBegin := 1; if IsSeparator(S[1]) > 0 then begin if KeepSeparators then begin iBegin := 2; A[iAdded] := S[1]; Inc(iAdded); end; end; for iStr := iBegin to iLenS do begin if not Quoted and (IsSeparator(S[iStr]) > 0) then begin if ExcludeEmpty and (iLenSS = 0) then begin if KeepSeparators then begin A[iAdded] := S[iStr]; Inc(iAdded); end; iBegin := iStr + 1; end else begin if AlreadyDQ then A[iAdded] := QuoteConcat else A[iAdded] := Copy(S, iBegin, iLenSS); AlreadyDQ := False; Inc(iAdded); if KeepSeparators and (iBegin > 0) then begin A[iAdded] := S[iStr]; Inc(iAdded); end else begin if LastIsSep and KeepSeparators and (iStr = iLenS) then begin A[iAdded] := S[iStr]; Inc(iAdded); end; end; iBegin := iStr + 1; iLenSS := 0; end; end else begin if IsQuote(S[iStr]) > 0 then begin if Quoted then begin Quoted := False; if iStr < iLenS then begin DoubleQuoted := IsQuote(S[iStr+1]) > 0; if AlreadyDQ then QuoteConcat := QuoteConcat + Copy(S, iBegin, iLenSS) + IfThen(DoubleQuoted, S[iStr+1], '') else QuoteConcat := Copy(S, iBegin, iLenSS) + IfThen(DoubleQuoted, S[iStr+1], ''); AlreadyDQ := AlreadyDQ or DoubleQuoted; end; end else begin Quoted := True; iBegin := iStr + 1; iLenSS := 0; end; end else begin if Quoted and (IsSeparator(S[iStr]) > 0) then Inc(iOffQuote); Inc(iLenSS); end; end; end; if iBegin <= iLenS then begin A[iAdded] := Copy(S, iBegin, MaxInt); Inc(iAdded); if LastIsSep and KeepSeparators then begin A[iAdded] := S[iLenS]; Inc(iAdded); end; end; if LastIsSep and not ExcludeEmpty then Inc(iAdded); if iAdded < iLenA then A := Copy(A, 0, iAdded); Result := Result - iOffQuote; end;Pour l'esprit sportif, il y a aussi la PCHAR, pas de grande différence de perf, je n'ai fait que la Lazy, faut être honnête, la explode complète en PChar ne fonctionne pas ^_^ violation d'accès ...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 {* ----------------------------------------------------------------------------- la fonction ExplodeToStrings encapsule Explode pour renvoyer une liste. @param S Chaine à découper @param A Tableau de Chaine qui recevra la découpe @param Separators Caractères qui délimitent une chaine pour la découpe @param ExcludeEmpty Si True, les Chaines vides ne sont pas insérés dans le Tableau @param Quotes Caractères qui délimitent une chaine pour la découpe contenant des Separators @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !) ------------------------------------------------------------------------------ } function ExplodeToStrings(const S: string; L: TStrings; const Separators: string; ExcludeEmpty: Boolean = False; const Quotes: string = ''): Integer; var A: Types.TStringDynArray; iL: Integer; begin if Assigned(L) then begin Result := Explode(S, A, Separators, ExcludeEmpty, Quotes); L.Clear(); L.Capacity := Result; for iL := Low(A) to High(A) do L.Add(A[iL]); end else Result := -1; end;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 {* ----------------------------------------------------------------------------- la fonction ExplodeLazyPChar retourne un tableau de chaînes. Ce sont les sous-chaînes, extraites de S, en utilisant le séparateur Separator. @param S Chaine à découper @param A Tableau de Chaine qui recevra la découpe @param Separator Caractère qui délimitent une chaine pour la découpe @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !) ------------------------------------------------------------------------------ } function ExplodeLazyPChar(const S: string; out A: Types.TStringDynArray; Separator: Char): Integer; var J, K, L: integer; P, P2, PEnd: PChar; begin Result := 0; L := Length(S); P := Pointer(S); P2 := P; PEnd := P; Inc(PEnd, L); while P < PEnd do begin if P^ = Separator then begin Inc(Result); end; Inc(P); end; if S[L] = Separator then SetLength(A, Result) else SetLength(A, Result + 1); K := 0; J := 0; P := P2; while P < PEnd do begin if P^ = Separator then begin if K > 0 then begin SetLength(A[J], K); Move(P2^, PChar(A[J])^, K); K := 0; end; P2 := P; Inc(P2); Inc(J); end else begin Inc(K); end; Inc(P); end; if (P2 < PEnd) and (K > 0) then begin SetLength(A[J], K); Move(P2^, PChar(A[J])^, K); end; end;
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Merci ShaiLeTroll,
merci à tous,
Vous trouverez dans la FAQ, les sources ou les tutoriels, de l'information accessible au plus grand nombre, plein de bonnes choses à consulter sans modération![]()
Des tutoriels pour apprendre à créer des formulaires de planning dans vos applications Access :
Gestion sur un planning des présences et des absences des employés
Gestion des rendez-vous sur un calendrier mensuel
Importer un fichier JSON dans une base de données Access :
Import Fichier JSON
Bon, je remonte ce sujet, pour communiquer une nouvelle version de Explode pour gérer le cas suivant
ce qui est contenu entre la 6eme " et le 3eme ; est ignorée
Code : Sélectionner tout - Visualiser dans une fenêtre à part "00000001";"00000002";"00000003" ;"00000004";"00000005";"00000006";"00000007";"00000008";"00000009";"00000010";"00000011";"00000012"
Qu'en pensez-vous ?
Merci de me communiquer d'éventuels bugs
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210 {* ----------------------------------------------------------------------------- la fonction Explode retourne un tableau de chaînes. Ce sont les sous-chaînes, extraites de S, en utilisant le séparateur Separator. cela peut servir pour lire du CSV @param S Chaine à découper @param A Tableau de Chaine qui recevra la découpe @param Separators Caractères qui délimitent une chaine pour la découpe @param ExcludeEmpty Si True, les Chaines vides ne sont pas insérés dans le Tableau @param Quotes Caractères qui délimitent une chaine pour la découpe contenant des Separators, n'importe quel séparateur peut commencer et terminé une chaine, une quote doublée est considéré comme valeur un quote dans la chaine @param KeepSeparators Si True, A contient les chaines et les séparateurs mais pas les Quotes, sinon (par défaut) A ne contient que les Chaines. @param KeepTextAfterQuote Si True, le Texte entre une quote terminale d'un texte et un séparateur est conservée, la Quote comprise, sinon (par défaut) le Texte entre une quote terminale d'un texte et un séparateur est ignorées @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !) ------------------------------------------------------------------------------ } function Explode(const S: string; out A: Types.TStringDynArray; const Separators: string; ExcludeEmpty: Boolean = False; const Quotes: string = ''; KeepSeparators: Boolean = False; KeepTextAfterQuote: Boolean = False): Integer; var iLesSep: Integer; iLesQuote: Integer; function IsSeparator(C: Char): Integer; begin for Result := 1 to iLesSep do if C = Separators[Result] then Exit; Result := -1; end; function IsQuote(C: Char): Integer; begin for Result := 1 to iLesQuote do if C = Quotes[Result] then Exit; Result := -1; end; var iStr: Integer; iQuote: Integer; iLenS: Integer; iLenSS: Integer; iLenA: Integer; iAdded: Integer; iBegin: Integer; Quoted: Boolean; DoubleQuoted: Boolean; AlreadyDQ: Boolean; QuoteConcat: string; iOffQuote: Integer; LastIsSep: Boolean; LastIsQ: Boolean; ContinueToNextSeparator: Boolean; begin iLenS := Length(S); iLesSep := Length(Separators); if (iLenS = 0) or (iLesSep = 0) then begin SetLength(A, 1); Result := 0; A[Result] := ''; Exit; end; iLesQuote := Length(Quotes); for iQuote := 1 to iLesQuote do if IsSeparator(Quotes[iQuote]) > 0 then raise EParserError.CreateFmt('le Délimiteur "%s" ne peut pas être un Séparateur !', [Quotes[iQuote]]); Result := 0; iQuote := 0; for iStr := 1 to Length(S) do begin if IsSeparator(S[iStr]) > 0 then Inc(Result) else if IsQuote(S[iStr]) > 0 then Inc(iQuote); end; if Odd(iQuote) then raise EParserError.CreateFmt('Nombre de Délimiteur Incorrect : "%d" !', [iQuote]); LastIsSep := IsSeparator(S[iLenS]) > 0; LastIsQ := IsQuote(S[iLenS]) > 0; if KeepSeparators then iLenA := Result * 2 + 1 else iLenA := Result + 1; SetLength(A, iLenA); iLenSS := 0; iAdded := 0; Quoted := False; iOffQuote := 0; QuoteConcat := ''; AlreadyDQ := False; iBegin := 1; if IsSeparator(S[1]) > 0 then begin if KeepSeparators then begin iBegin := 2; A[iAdded] := S[1]; Inc(iAdded); end; end; ContinueToNextSeparator := False; for iStr := iBegin to iLenS do begin if ContinueToNextSeparator and (IsSeparator(S[iStr]) <= 0) then Continue; ContinueToNextSeparator := False; if not Quoted and (IsSeparator(S[iStr]) > 0) then begin if ExcludeEmpty and (iLenSS = 0) then begin if KeepSeparators then begin A[iAdded] := S[iStr]; Inc(iAdded); end; iBegin := iStr + 1; end else begin if AlreadyDQ then A[iAdded] := QuoteConcat else A[iAdded] := Copy(S, iBegin, iLenSS); AlreadyDQ := False; Inc(iAdded); if KeepSeparators and (iBegin > 0) then begin A[iAdded] := S[iStr]; Inc(iAdded); end else begin if LastIsSep and KeepSeparators and (iStr = iLenS) then begin A[iAdded] := S[iStr]; Inc(iAdded); end; end; iBegin := iStr + 1; iLenSS := 0; end; end else begin if IsQuote(S[iStr]) > 0 then begin if Quoted then begin Quoted := False; if iStr < iLenS then begin DoubleQuoted := IsQuote(S[iStr+1]) > 0; if not KeepTextAfterQuote and not DoubleQuoted and (IsSeparator(S[iStr+1]) <= 0) then begin ContinueToNextSeparator := True; iQuote := iStr; end; if AlreadyDQ then QuoteConcat := QuoteConcat + Copy(S, iBegin, iLenSS) + IfThen(DoubleQuoted, S[iStr+1], '') else QuoteConcat := Copy(S, iBegin, iLenSS) + IfThen(DoubleQuoted, S[iStr+1], ''); AlreadyDQ := AlreadyDQ or DoubleQuoted; end; end else begin Quoted := True; iBegin := iStr + 1; iLenSS := 0; end; end else begin if Quoted and (IsSeparator(S[iStr]) > 0) then Inc(iOffQuote); Inc(iLenSS); end; end; end; if iBegin <= iLenS then begin if ContinueToNextSeparator then A[iAdded] := Copy(S, iBegin, iQuote - iBegin) else if LastIsQ then A[iAdded] := Copy(S, iBegin, iLenS - iBegin) else A[iAdded] := Copy(S, iBegin, MaxInt); Inc(iAdded); if LastIsSep and KeepSeparators then begin A[iAdded] := S[iLenS]; Inc(iAdded); end; end; if LastIsSep and not ExcludeEmpty then Inc(iAdded); if iAdded < iLenA then A := Copy(A, 0, iAdded); Result := Result - iOffQuote; end;
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Normalement de quoi gagner un pouième de seconde sur les chaînes un peu plus longues:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 function ExplodeLazy(const S: string; out A: Types.TStringDynArray; Separator: Char): Integer; var I, J, K: Integer; iLenS: Integer; begin Result := 0; iLenS := Length(S); if iLenS = 0 then begin SetLength(A, 1); A[0] := ''; Exit; end; SetLength(A, iLenS); K := 1; J := 0; for I := 1 to iLenS do if S[I] = Separator then begin if K <> I then A[J] := Copy(S, K, I - K); Inc(J); K := I + 1; end; if K <= iLenS then A[J] := Copy(S, K, MaxInt); Result := J - 1; SetLength(A, Result); end;
GoustiFruit, tu nous déterre un sujet !
Tu as retiré le pré-calcul de la longueur !
Tu n'as pas tord, en fait mon code est optimisé pour D6 !
Cette technique prédictive est obsolète !
Dès que l'on utilise FastMM (ou dès D2007, je crois), le SetLength est environ 100 à 1000 fois plus rapide pour la ré-allocation d'un tableau que le Gestionnaire Mémoire de D6 !
Par contre, si tu as une chaine Longue, tu vas donc utiliser la taille de la chaine pour allouer A un tableau de chaine !
Donc, pour une chaine longue, disons plus de 1Ko, tu vas allouer inutilement un tableau de 1000 éléments
Une chaine est un pointeur sur une structure
Donc ton tableau, devrait faire 4Ko en 32Bits et 8Ko en 64Bits !
Dans le genre consommation mémoire inutile, c'est un chef-d’œuvre !
Et mon exemple n'est qu'un 1Ko, pour une chaine de 1Mo ça passe à 4Mo et 8Mo de consommation superflue pour un pauvre Explode !
Fausse bonne idée !
A la limite, en FastMM il est préférable de ré-allouer le tableau, cela sera plus rapide que la prédiction !
On peut aussi allouer par tas de 16, en utilisant une TStringList à la place d'un Array of string, cela est géré nativement via sa méthode interne Grow !
J'ignore comme fonctionne exactement la fonction _Initialize pour un array of string, je pense qu'elle économise la mémoire en affectant nil comme pointeur de chaine qui simule une chaine vide, d'où juste 4 ou 8 octet par cellule du tableau !
Heureusement car rappelons qu'une chaine sans même compter les caractères consomment de la mémoire
D7 = 12 octet, pour le compteur de référence, la longueur et le pointeur lui même
DXE2 = 15 octet (je crois), le code page, la longueur du char, le compteur de référence, la longueur et le pointeur lui même
Heureusement que _Initialize n'alloue pas une chaine pour chaque cellule, ça sera effectivement idiot car dans le cas contraire, la consommation mémoire pour une chaine de 1Ko passerait à 8Ko pour chaine Unicode et 15Ko pour une chaine Ansi (taille divisé par deux pour le nombre de char), et ça monterait à 10Ko et 19Ko pour du 64Bits !
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
J'ai autre chose dans les tuyaux, je fais quelques vérifications et je poste ;-)
Petite remarque en passant: pour moi un séparateur est sensé... *séparer*, et oui !, 2 valeurs donc dans les cas suivants:
- '' (chaine vide) -> la fonction doit retourner 0 pour nombre de séparateurs, et une chaîne vide dans le tableau.
- ';' -> la fonction doit retourner 1 pour le nombre de séparateurs et 2 chaînes vides dans le tableau !
- 'A;B -> la fonction doit retourner 1 pour le nombre de séparateurs et les 2 chaînes 'A' et 'B' dans le tableau
- 'A;B;' -> la fonction doit retourner 2 pour le nombre de séparateurs et les 3 chaînes 'A' et 'B' et '' (vide) dans le tableau
- etc.
Ce que je veux dire c'est qu'un séparateur en fin de ligne devrait logiquement indiquer qu'il reste une valeur vide en fin de chaîne ! Si ce n''est pas le cas, c'est que le fichier d'origine a été mal formaté.
PS: la fonction est sensée être la plus rapide possible, on n'a jamais parlé de l'utilisation mémoire![]()
Ce n'est pas forcément du CSV !
Sinon une ligne CSV fini forcément par un ; !
Ce qui est après le dernier ; est du commentaire et non de la donnée, c'est une convention très mal respectée !
Pour le Result, oui strictement le nombre de séparateur et le nombre de case !
C'était aussi ma vision des choses !
Le cas de ';', je n'y avais jamais pensé, ça doit donner 1 et 1 !
Rappelons que c'est ExplodeLazy !
La version gérant les valeurs vides, les répétitions de séparateurs ... c'est Explode tout court !
Version pour FastMM
Faudrait justement vérifié, allouer la mémoire, cela prend du temps, mettre à nil toute cette mémoire aussi !
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 {* ----------------------------------------------------------------------------- la fonction ExplodeLazy retourne un tableau de chaînes. Ce sont les sous-chaînes, extraites de S, en utilisant le séparateur Separator. @param S Chaine à découper @param A Tableau de Chaine qui recevra la découpe @param Separator Caractère qui délimitent une chaine pour la découpe @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !) ------------------------------------------------------------------------------ } function ExplodeLazy(const S: string; out A: Types.TStringDynArray; Separator: Char): Integer; var I, J, K: integer; iLenS, iLenA: integer; begin iLenS := Length(S); if iLenS = 0 then begin SetLength(A, 1); Result := 0; A[Result] := ''; Exit; end; iLenA := 16; SetLength(A, iLenA); K := 1; Result := 0; for I := 1 to iLenS do begin if S[I] = Separator then begin if Result >= iLenA then begin iLenA := J + 16; SetLength(A, iLenA); end; if K <> I then A[Result] := Copy(S, K, I - K); Inc(Result); K := I + 1; end; end; J := Result; if K <= iLenS then begin if J >= iLenA then begin iLenA := J + 1; SetLength(A, iLenA); end; A[J] := Copy(S, K, MaxInt); Inc(J); end; if J < iLenA then SetLength(A, J); end;
Je pense qu'au final, cela peut se montrer coûteux en temps !
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Ce qui me chagrine dans ton code c'est que tu parcours deux fois la chaîne; or quand on fait le découpage mentalement ou sur sa feuille de papier, d'instinct on ne parcourt qu'une seule fois cette chaîne... Alors j'ai pensé à ça:
A modifier légèrement, donc, si on considère que le dernier séparateur ne sépare... rien.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 function ExplodeLazy(const S: string; out A: TStringDynArray; Separator: Char): Integer; var aSepPos: TIntegerDynArray; i1, iStart, iEnd, iLenS: Integer; begin Result := 0; iLenS := Length(S); SetLength(aSepPos, iLenS); for i1 := 1 to iLenS do begin if S[i1] = Separator then begin aSepPos[Result] := i1; Inc(Result); end; end; SetLength(A, Result + 1); iStart := 1; for i1 := 0 to Result - 1 do begin iEnd := aSepPos[i1]; if iEnd > iStart then A[i1] := Copy(S, iStart, iEnd - iStart); iStart := iEnd + 1; end; A[Result] := Copy(S, iStart, MaxInt); end;
PS: un tableau d'entiers ça consomme moins qu'un tableau de chaînes non ? ;-)
Oui, je sais, mais on revient toujours aux fondamentaux, et hier je cherchais une fonction pour découper une url en ses sous-segments, et de fil en aiguille, ...GoustiFruit, tu nous déterre un sujet !![]()
Ce qui te chagrine mais justement tout l'originalité de ces fonctionnalités conçus pour D4 à D7, je l'ai d'ailleurs mentionné depuis le début !
Comme je l'ai dit c'était une "technique prédictive", car parcourir deux fois était plus rapide que de réallouer !
Ce n'est plus vrai avec FastMM et je l'avais déjà fait remarquer dans le sujet : Création d'objets "par lots" ou dans Temps pour parser un fichier de + de 3 millions de lignesSi tu lit mon nouveau code, je fais une ré-allocation par bloc !
Que vient faire là un Tableau d'entier, normalement, un tableau de chaine vide, consomme la même chose qu'un tableau d'entier, évidement en 32bits !
Dès que tu commence à allouer des chaines, le tableau lui consomme toujours QUE les pointeurs, ensuite c'est les chaines qui consomment par leur Structure en @ négative puis les données
Ne pas confondre le tableau qui ne stocke que les pointeurs, et la mémoire consommée par les pointeurs eux-même !
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Ah tu n'as pas vu, en fait j'ai un peu changé la méthode, le tableau d'entier sert à stocker la position des séparateurs, ensuite on parcourt uniquement ce tableau pour découper la chaîne d'indice en indice.
Pourrais-tu me pointer vers le message où tu donnes ta technique pour chronométrer cette fonction, mon chronométrage donne un peu trop de fluctuations, je fias un simple:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 var t1, t2: Int64; StrArray: TStringDynArray; s: string; i1: Integer; begin s := '0;;1;2!2;333;44!44;55555;666!666;7777777;8888!8888;999999999;00000!00000;"AAA";"BBB";"CCC"'; t1 := DSiTimeGetTime64; for i1 := 0 to 99999 do ExplodeLazy(s, StrArray, ';'); t2 := DSiTimeGetTime64; AddToLog(Format('100000 x ExplodeLazy exécuté en %d ms', [t2-t1]));
Ah oui, bonne idée, c'est ce que j'utilise dans SearchString InFile et SearchBinaryInFile
Mémoriser les offsets pour ensuite pouvoir y retourner !
Mais en terme de consommation, cela ne doit pas changer grand chose !
Tu consomme toujours un tableau de 4 fois la taille chaine !
Fait donc des mesures avec QueryPerformance Counter\Frequency
Compare les Trois !
La version SetLength de A
La version SetLength de aSepPos
La version SetLength + 16 que j'ai proposé (mais pas du tout testé, ce n'est pas exempte de coquille)
EDIT : Lol, je n'avais même pas vu ta demande pour le chrono, je le l'ai ajouté de moi même ! On pense pareil !![]()
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Mouais, ta méthode de mesure me donne les mêmes fluctuations :-\
Un meilleur test serait de vérifier sur un jeu de chaînes très différentes et suffisamment nombreuses...
Oups, en effet coquillette en vue. Violation d'accès sur un "Inc(Result)", je vais enquêter...
Edit: à vue de nez, remplacer...
pariLenA := J + 16;
???Inc(iLenA, 16);
tu peux remplacer par iLenA := Result + 16; ou par Inc(iLenA, 16);.
On peut même raccourcir en ceci, on gagne une allocation en cas de chaine ne contenant pas de séparateur
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 {* ----------------------------------------------------------------------------- la fonction ExplodeLazy retourne un tableau de chaînes. Ce sont les sous-chaînes, extraites de S, en utilisant le séparateur Separator. @param S Chaine à découper @param A Tableau de Chaine qui recevra la découpe @param Separator Caractère qui délimitent une chaine pour la découpe @return Nombre de Séparateur Trouvé (peut-être différent du nombre de chaine dans A !) ------------------------------------------------------------------------------ } function ExplodeLazy(const S: string; out A: Types.TStringDynArray; Separator: Char): Integer; var I, J, K: integer; iLenS, iLenA: integer; begin iLenS := Length(S); if iLenS = 0 then begin SetLength(A, 1); Result := 0; A[Result] := ''; Exit; end; K := 1; Result := 0; iLenA := 0; for I := 1 to iLenS do begin if S[I] = Separator then begin if Result >= iLenA then begin iLenA := Result + 16; SetLength(A, iLenA); end; if K <> I then A[Result] := Copy(S, K, I - K); Inc(Result); K := I + 1; end; end; J := Result; if K <= iLenS then begin if J >= iLenA then begin iLenA := J + 1; SetLength(A, iLenA); end; A[J] := Copy(S, K, MaxInt); Inc(J); end; if J < iLenA then SetLength(A, J); end;
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Bah chez moi les temps d'exécution varient du simple au double pour chacune des 3 versions, en gros de 110ms à 220ms pour 100000 itérations. Impossible dans ces conditions de déterminer un vainqueur.
Merci d'avoir passé ta matinée là-dessus![]()
Partager