kurayamino
Messages postés3Date d'inscriptionsamedi 21 décembre 2002StatutMembreDernière intervention18 décembre 2005 18 déc. 2005 à 11:40
je suis loin d'etre un pro en Delphi, mais bon, moi j'ai codé la fontion comme ci dessous :
function GetTok(Chaine: String; Position: byte; Separateur: Char):String;
var
i:byte;
temp:string;
begin
{ initialise les variables }
i := 1;
temp := chaine;
{ si 1er token }
if position = 1 then begin
{ si la fin de la chaine est un separateur }
if pos(separateur,chaine) > 0 then begin
result := copy(chaine,1,pos(separateur,chaine)-1);
exit;
{ sinon }
end else begin
result := chaine;
exit;
end;
{ sinon }
end else begin
{ effacer le 1er token jusqu'a atteindre la position voulue }
{ utilisation d'une variable temp à la place de chaine }
while i < position do begin
delete(temp,1,pos(separateur,temp));
i := i + 1;
end;
{ si la fin de la chaine est un separateur }
if pos(separateur,temp) > 0 then begin
result := copy(temp,1,pos(separateur,temp)-1);
exit;
{ sinon }
end else begin
result := temp;
exit;
end;
end;
end;
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 14 déc. 2005 à 11:12
@Ahmed : desolé j'avais pas vus l'une de tes reponses...
Foxi->Désolé de ne pas avoir envisagé des fichiers spéciaux comportant des séparateurs à plusieurs caractères ... Pour ce qui est de copier une fonction d'une unit, celà me semble extrêment intéressant et devrait faire l'objet d'un petit tutoriel de ta part sur les pièges à éviter.
pour les separateurs multicaractere je pense en premier lieu au fin de ligne (EOLN) dans un fichiers texte par exemple (#13+#10), chainepos peut permettre de renvoyer toute une ligne d'un tel fichier.
mais egalement pour les fichiers binaire, qui par exemple utilise un h0000 ou encore h00000000 pour separer certain element tel par exemple les IdTag des MP3 ect...
ce qui permet a ChainePos d'avoir une utilitée réellement plus vaste.
un exemple concret, on recupere dans un stream la partie du fichier binaire qui nous interresse, et un simple apel a ChainePos nous permet de recuperer directement ce qui nous interresse et la mon astuce "const posi : cardinal = 1" vas nous servir.
Avec READ, on recupere un Char ou un tableau ... string n'est rien d'autre qu'un type PChar "humanisé".
admettons que le stream contient plusieurs partie avec données a taille variable et separateurs egalement variable.
recuperons les elements grace a ChainePos :
ABCD = ChainePos(#0+#0, SPart)
EF = ChainePos(#0+#0, SPart, 2)
GHI = ChainePos(#0+#0+#0+#0, SPart, 2)
JKLM = ChainePos(#2, SPart, 2)
NOP = ChainePos(#32+#32, ChainePos(#0+#0, SPart, 5)) << 5 parce que #0#0#0#0 est consideré comme deux separateur #0#0 et #0#0 avec un element vide au milieu.
QRS = ChainePos(#32+#32, ChainePos(#0+#0, SPart, 5), 2) << idem que pour NOP
tout cela sans toucher au stream ou a la position de l'offset de lecture dans le stream.
vus les performances que j'ai obtenus avec ma version de ChainePos utilisé dans un Listbox pour transformer le texte affiché par rapport au texte contenus ... je pense que ça reste carrement correct (je rapel que 100000 requette a ma methode prennent environ 63ms en moyenne seulement).
bon la l'exemple est bidon... mais l'utilisation de chainepos de cette façon reste une possibilitée a prendre en compte.
par contre les autre proposition a separateur uni-caractere ne peuvent pas etendre leur utilisation jusque la.
donc comme l'a dit JLen100 a propos de ma methode, non seulement plus generale mais egalement a mon sens trés polyvalente. ce qui est forcement un plus car elle peut etre utilisée la ou on s'y attend le moins.
Tu prend notre ChainePos et Format et tu as quasiment plus besoin des autres fonctions de traitements de chaines de caractere. (qu'elle pretention! hihihi)
d'ailleur je m'etonne qu'une fonction aussi pratique que ChainePos n'existe pas dans delphi nativement. dans php il existe une fonction a peu prés equivalente GetWord() il me semble.
(vend methodes delphi pas cher ... bon etat general ... prix a negocier ... hehehe)
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 14 déc. 2005 à 10:35
-->f0xi
pour le portage ce que tu dis est vrai tant que tu n'appelles pas de ressource du bios ou d'entrées sorties.
pour le timer je t'expliquerais ça en message perso vu que ce n'est pas le sujet ici (la solution c'est de travailler en asynchrone pour ne jamais attendre et de traiter l'information quand elle est complète).
mon propos c'était simplement de dire que si windows gére le mapping mémoire il peut être mis en défaut sur des problémes de timming (en gros et pour faire simple quand une tache est longue quand elle rend la main windows n'est plus capable d'exécuter celles qui se sont accumulées.
pour le systeme windows c'est Xp pro
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 14 déc. 2005 à 10:32
tiens je viens de verifier :
vus que l'interet apparement est de ne pas augmenter la taille de la close uses :
on extrait PosEx de strutils et on la place dans l'unité principale.
copy, pos et length sont incluse dans l'unité system (qui ne necessite pas qu'on la declare)
donc avec ma methode on obtient certe deux fonctions au lieu d'une (posex et chainepos)
tout en gardant les perf et les avantages de ma version.
alors revoila une version "compressée non lisible" de la fonction (sans avoir a declarer une unité en plus dans les uses) :
function ChainePos(const Sep, Dans: String; const posi: cardinal = 1): String;
function e_PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer;
var I, X, 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;
var i,p1,p2 : integer;
begin
result :''; if (Dans[1] Sep) and (posi = 1) then exit;
p1 := 1; for i := 1 to posi-1 do begin
p1 :e_posex(sep,dans,p1); if p1 0 then exit else p1 := p1 + length(sep); end;
p2 :e_posex(sep,dans,p1); if p2 0 then p2 := length(Dans)+1;
result := copy(dans,p1,p2-p1);
end;
(je tiens a la vendre cette methode ou quoi ^^ lol)
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 14 déc. 2005 à 09:59
JLen100 >> "sans parler de la stabilité de windows"
Heu ... l'assembleur c'est la base de toute machine, je ne vois pas pourquoi Windows n'accepterais pas trop cela vus que tout programme compiler est forcement retranscrit en assembleur.
puis l'assembleur est relativement bien portable vus qu'on agit directement sur le Proco.
bien entendus, il faudrat tenir compte du type de CPU et du jeux d'instruc supporté par le CPU... le seul probleme qu'il y auras serat sur un portage asmX86 vers Motorola et autre.
pour ce qui est des boucles, forcement si tu fait pas gaffe a la sortie ... que ça soit en n'importe quel langage ça reviendras a faire freezer l'appli ce qui est toujours mauvais.
pour le truc du timer qui attend ... la je dirais, mauvaise methode! il faut utiliser les evenements plutot qu'un timer (bien que je ne connaisse pas réellement le probleme exposé).
mais un timer pour ça je dirais = ressource gachée inutilement.
et puis je vois mal quel dev aurait envie de créer une boucle infinie aucun interet de developper un bugware, windows suffit.
question, sous quel systeme windows est tu ?
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 13 déc. 2005 à 11:13
pas toujours BruNews puisque qu'il n'est arrivé récemment de planter windows sur un while dont j'avais oublié de mettre à jour la condition de sortie: ce n'est pas une simple condition de mapping mais également un problème de timming (il ne faut pas oublier que windows n'est réellement multiltache)
autre exemple: attendre dans un timer une chaine de caractères sur la liaison série rend windows instable.( au mieux ce sont des problèmes d'affichage au pire le plantage)
enfin en assembleur rien de plus facile que de faire un infinit loop sans erreur d'adressage et là windows n'y voit goute!!
BruNews
Messages postés21040Date d'inscriptionjeudi 23 janvier 2003StatutModérateurDernière intervention21 août 2019 13 déc. 2005 à 10:58
Traiter un buffer, que soit une chaine ou autre, en ASM ou quelque autre langage que ce soit n'entamera en rien la stabilité de Windows. Le prog est dans SON propre espace de mémoire virtuelle et se verra démappé illico par le système avec l'injureBox habituelle s'il commet une erreur d'adressage, rien de plus.
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 13 déc. 2005 à 10:47
-->foxi c'est normal que la performance soit dégardé puisqu'à chaque boucle tu as creation et destruction d'une chaine dynamique mais si tu déclares ta chaine comme string[255] ta performance sera meilleure (dans ce cas l'espace est réservé à l'initialisation de la fonction) mais moins bonne que si tu travailles sur les index (nombre d'instructions exécutées superieur)(ou les pointeurs qui est la méthode la plus rapide) mais si l'on veut vraiment aller encore plus vite on peut aussi travailler au niveau de l'assembleur; mais bonjour la portabilité!!! sans parler de la stabilité de windows puisque sur une procédure un peu longue on pertube la gestion des streams.
--> AMEHDOUDALI pour le tuto ça a été un peu laborieux : je ne devrais jamais programmer avant et après une teuf!!
pour l'utilisation des unités il me semblait qu'à la compilation DELPHI n'intégrait que les fonctions et procédures utilisées et que les autres étaient ignorées. (c'est ce que faisait Turbo Pascal)
cs_MAURICIO
Messages postés2106Date d'inscriptionmardi 10 décembre 2002StatutModérateurDernière intervention15 décembre 20145 13 déc. 2005 à 10:20
Merci pour la pub Ahmed Boudali!
AhmedBoudali
Messages postés14Date d'inscriptionmardi 24 octobre 2000StatutMembreDernière intervention13 décembre 2005 13 déc. 2005 à 10:10
Je conseille un petit tour intéressant du côté du portugal avec les sources de Mauricio.
Jlen100->Je remercie pour l'excellent tuto sur les lecture/ecriture de bits (voir forum).
Foxi->Désolé de ne pas avoir envisagé des fichiers spéciaux comportant des séparateurs à plusieurs caractères (Champ1CeciestseparateuroptimisestandardChamp2Ceciestseparateuroptimisestandard !). Pour ce qui est de copier une fonction d'une unit, celà me semble extrêment intéressant et devrait faire l'objet d'un petit tutoriel de ta part sur les pièges à éviter. Pour ma part, je teste cette éventualité.
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 12 déc. 2005 à 18:33
alors, y'a eu pas mal de post depuis la derniere fois.
@JLen100 : les performances sont fortement degradée a partir du moment ou on utilise ceci :
for x := S to F do string := string + char[x];
constatation generale... no comment. c'est lourd. et le seul moyen de gagner en perf et d'utiliser les pointeurs, ce qui bien sur (et je ne comprend toujours pas pourquoi) n'est pas conseiller avec Dot Net.
@Ahmed : afin de ne pas accroitre les declaration uses (d'ailleur je vois pas ou est le probleme) on peu extraire la fonction Posex() de l'unité StrUtils.
function PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer;
var I, X, 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;
Copy et Length sont incluse dans l'unité System qui est presque "obligatoire" la plupart du temps. donc ça ne changeras rien de les extraires de cette derniere.
On oblige a utiliser un nombre positif car il serait illogique de demander des elements en dehors du champs 1..N. tout simplement.
Pour ta fonction, et meme certaines autres propositions, il y a soit un probleme de performance, soit un probleme de souplesse (separateur a caractere unique)...
DelphiProg a bien cerné l'utilisation plus generale de ma fonction. si maintenant cette fonction ne vas servir que dans ce programme pour une utilisation bien precise, voir les autres solutions proposées.
Pour ton soucis d'augmenter les declarations dans la close Uses, tu peut toujours te créer une unité d'alias, cela reviendras au meme mais diminueras la close principale ^^
@Emhandal : les labels sont assé vieux, mais depuis que le pascal n'est plus un langage lineaire ils n'ont plus raisons d'etre. bon ça peut toujours etre utile dans certain cas rare, mais un debutant devrais apprendre a programmer en delphi sans les utiliser.
tiré de l'aide delphi :
L'utilisation de l'instruction goto est généralement déconseillée en programmation structurée. Elle peut néanmoins être utilisée dans certains cas pour sortir de boucles imbriquées.
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 11 déc. 2005 à 13:18
he oui on ne peut récuperer l'octet 0 que si on déclare la chaine en string[xx] (shortstring)
Emandhal
Messages postés194Date d'inscriptiondimanche 2 mars 2003StatutMembreDernière intervention10 octobre 20063 11 déc. 2005 à 13:09
bah oui c'est normal, une chaine commence toujours à 1, si tu lui sors un 0 ilne va pas être content...
De plus c'est par octets, le Dans[0] serait valable que dans un ShortString (qui se limite qu'à 255 caractères). Pour les string il faut prendre Dans[-3]..Dans[0] vu que la chaine peut aller jusqu'a High(Integer)...
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 11 déc. 2005 à 13:04
integer(Dans[0])ne fonctionne pas non sur une varaiable dynamique non initialisée (erreur de compilation élément 0 inaccessible comme pour ord() )
Emandhal
Messages postés194Date d'inscriptiondimanche 2 mars 2003StatutMembreDernière intervention10 octobre 20063 11 déc. 2005 à 12:55
Integer(Dans[0]) ^^
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 11 déc. 2005 à 12:19
mille excuses ord() ne fonctionne pas avec les variables dynamiques non affectées.
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 11 déc. 2005 à 11:57
j'ai oublier fais ord(Dans[0]) pour transtyper le char en integer
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 11 déc. 2005 à 11:54
pour length() lis le caractere 0 ---> Dans[0]:= longueur de la chaine.
AhmedBoudali
Messages postés14Date d'inscriptionmardi 24 octobre 2000StatutMembreDernière intervention13 décembre 2005 11 déc. 2005 à 11:48
Merci JLen100 pour ces remarques.
*J'ai maintenu b pour une lecture aisée
Pour Tout le monde
*Je cherche un moyen d'éviter les fonctions et donc m'approcher d'une version automate(reste length()). Une idée ?
D'avance merci.
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 11 déc. 2005 à 11:31
pour déterminer la position du premier séparateur tu peux aussi utiliser la fonction pos (unité system) qui te renvoie la positon du premier séparateur trouvé dans la chaine debut=pos(sep,Dans);
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 11 déc. 2005 à 11:21
-->AHMEDBOUDALI
plutot que de faire:
if (b=Sep)then mets plutot if (Dans[i]=Sep)then...
et utilises un index de début et un index de fin et avant de sortir tu fais:
result:= copy(Dans,debut,fin-debut) tu éviteras les réallocations de mémoire.
nota copy et length(que tu utilises) font parties de l'unité system.
AhmedBoudali
Messages postés14Date d'inscriptionmardi 24 octobre 2000StatutMembreDernière intervention13 décembre 2005 11 déc. 2005 à 10:47
Mes amis,
Je dois convenir que vos remarques sont empreintes d'une maîtrise que j'espère atteindre.
*La petite histoire de ce code (en dehors de ressusciter le basic) est un pari avec un collègue d'arriver à un automate, avec le moins de lignes (il en a horreur) et à traiter tous les cas de figure possibles sur une ligne d'un fichier texte délimité :
--> Ahmed/Boudali/ ==>si 1 alors Ahmed,si 3 alors vide
--> /Ahmed/Boudali/ ==>si 1 alors vide,si 2 alors Ahmed
--> /Ahmed//Boudali/ ==>si 1 alors vide,si 3 alors vide
--> /Ahmed/Boudali ==>si 1 alors vide,si 4 alors vide
*L'utilisation de length(), posex(), copy() oblige à accroître la liste "Uses".
*Pourquoi forcer l'entrée d'un numéro de position positif (format byte ou word) ?
*Reste la performance et les allocations de mémoire pour string, là mon code pêche... Effectivement, les variables "Sep" et "b" doivent être déclaré en char, la variable "a" doit être remplacé par Result et Result a une longueur maximum équivalent à celle de la valeur entrée ("Dans").
Merci encore pour ces pertinentes remarques.
cs_Delphiprog
Messages postés4297Date d'inscriptionsamedi 19 janvier 2002StatutMembreDernière intervention 9 janvier 201332 10 déc. 2005 à 19:52
Beau travail les gars et discussions intelligentes.
Ca commençait à nous manquer ces derniers temps, n'est-ce pas foxi ? ;o)
J'attire votre attention tout de même sur le fait que l'usage des pointeurs n'est pas bienvenu sous .Net. Si vous devez porter votre code pour qu'il fonctionne dans cet environnement, vous devrez alors privilégier la solution proposée par foxi.
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 10 déc. 2005 à 18:27
c'est vrai que quelques commentaires auraient été utiles. Mais on voit quand même que tu travailles avec les pointeurs.
deux petites remarques
- la chaine ne doit pas contenir de #0 ( Pchar à 0 terminal)
- la routine de foxi est plus générale ( pas de limitation sur la longueur du séparateur)
mais à part ces détails joli travail!!
@+
jlen
Emandhal
Messages postés194Date d'inscriptiondimanche 2 mars 2003StatutMembreDernière intervention10 octobre 20063 10 déc. 2005 à 18:17
j'avoue ce dernier post est pas des plus utile... pas d'explications, pas de commentaires... et je m'en excuse :)
Emandhal
Messages postés194Date d'inscriptiondimanche 2 mars 2003StatutMembreDernière intervention10 octobre 20063 10 déc. 2005 à 18:06
aller, j'aime les défis d'optimisation ^^
ma version delphi :
Function SubString(aStr: String; aSepar: Char; n: Integer): String;
{===============================================================================}
{ renvoie ce qui est à gauche de la droite de la n ieme sous chaine séparée par }
{ aSepar de la chaine aStr }
{ ex : SubString('TMP|c:\rep1\reptmp\', '|', 1) renvoie 'c:\rep1\reptmp\' }
{ Permet d'extraire un à un les éléments d'une chaine séparés par un séparateur }
{===============================================================================}
Var
pStr, pRes, pStart: PChar;
Nb: Integer;
Function PosChr(aString: PChar; aChr: Char): PChar;
Begin
Result := nil;
While aString^<>#0 do
begin
If aString^=aChr Then
begin Result := aString; Exit; end;
Inc(aString);
end;
End;
Begin
Result := '';
If n<0 Then Exit;
aStr := aStr+aSepar;
Nb := 0;
pStr := PChar(aStr);
While pStr^<>#0 do
begin
pRes := PosChr(pStr, aSepar);
If pRes=nil Then Exit;
If Nb=n Then
begin System.SetString(Result, pStr, pRes-pStr); Exit; end;
pStr := pRes+1;
Inc(Nb);
end;
End;
c'est une vieille routine que j'ai adaptée pour gérer uniquement des caractères comme séparateur
f0xi -> tu serais étonné si tu voyais certains de mes codes... j'ai deja mis des goto qui font gagner en rapiditée dans mes codes :)
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 10 déc. 2005 à 07:57
salut foxi,
pour le manque de performance du code je pense (mais je ne l'ai pas tester)que cela viens surtout des affectations des strings (a et b).
je m'explique quand on uilise un string en variable locale c'est toujours une variable dynamique donc a chaque fois qu'une modification de la variable affecte sa longueur le programme travaille comme ça:
appel au gestionnaire de tas pour reserver l'espace mémoire pour la taille de chaine
concatenetion de la chaine initiale et de l'ajout
libération de la mémoire occupée par la chaine initiale
il est donc préférable de considérer la chaine comme un tableau et travailler sur des pointeurs dans ce tableau (ce que fait posex)
c'est aussi pour cela que quand on le peut il vaut mieux affecter une longueur fixe à une chaine par l'utilisation de string[xx] le commpilateur reserve alors un espace mémoire fixe
il en est de même quand on déclare une variable de type scalaire (char, integer, byte......).
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 10 déc. 2005 à 01:36
Lut ami jlen!
alors pour le petit probleme, il n'est pas voulus. c'est en fait posex qui procede de cette maniere.
en effet, d'accord avec toi sur le fait que ce n'est pas trés "delphi".
donc voila un correctif qui ne degrade meme pas les performance de la routine :
function ChainePos(const Sep, Dans: String; const posi: byte = 1): String;
var i,p1,p2 : integer;
begin
result := '';
if (Dans[1] = Sep) and (posi = 1) then begin
exit;
end;
p1 := 1;
for i := 1 to posi-1 do begin
p1 := posex(sep,dans,p1);
// permet de stoper si on vas au dela de la taille de la chaine.
if p1 = 0 then begin
exit;
end
// sinon on continus.
else begin
p1 := p1 + length(sep);
end;
end;
p2 := posex(sep,dans,p1);
if p2 = 0 then p2 := length(Dans)+1;
result := copy(dans,p1,p2-p1);
end;
voila, conforme a l'esprit delphi.
jlen100
Messages postés1606Date d'inscriptionsamedi 10 juillet 2004StatutMembreDernière intervention25 juillet 201413 9 déc. 2005 à 23:29
-->foxi,
en cas de dépassement il serait plus judicieux de renvoyer une chaine vide (c'est plus conforme à l'esprit DELPHI et à la logique: si on est au dela c'est qu'on a pas trouver l'élément d'autre part si tu retournes le premier terme il y a ambiguité avec la requete du mot 1 ce qui ne facilite pas l'utilisation.
-->AhmedBoudali
plutot que d'utiliser un GOTO antediluvien tu aurais pu écrire:
if (Dans[1]=Sep) and (posi=1) then
begin
Result:=a;
exit;
end;
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 9 déc. 2005 à 23:06
Holala! un bon vieux GOTO! le Basic c'est finis mon ami!
tu peux faire comme cela :
uses strutils; //pour posex
function ChainePos(const Sep, Dans: String; const posi: byte = 1): String;
var i,p1,p2 : integer;
begin
result := '';
// si le premier element n'est pas definis on sort tout de suite
if (Dans[1] = Sep) and (posi = 1) then exit;
// on initialise P1 a 1
p1 := 1;
// on recherche la position du separateur N dans la chaine
for i := 1 to posi-1 do p1 := posex(sep,dans,p1)+length(sep);
// on recherche la position du prochain separateur aprés P1
p2 := posex(sep,dans,p1);
// si on l'a pas trouver c'est qu'on est surrement en fin de chaine
// P2 devient donc la taille de la chaine+1.
if p2 = 0 then p2 := length(Dans)+1;
// on renvois le resultat en utilisant la fonction copy
result := copy(Dans, p1, p2-p1);
end;
Cette methode est beaucoup plus rapide; 68ms de moyenne pour ma methode contre 1900ms de moyenne pour la tienne sur 100000 requettes. Les performances de tes programmes s'en trouverons donc grandement ameliorée.
La supression des variables A et B permettras aussi de gagner en memoire.
d'ailleur dans ta methode, B aurait du etre declarée en Char et non en String.
l'ecriture de "const posi : integer = 1" permet de ne pas definir posi si on desir le premier mot de la chaine. ce qui pourrait etre utile dans certaine utilisation.
d'ailleur sur ce point, posi doit etre de type Byte (0..255) pour eviter les nombre negatif.
cela laisse une large manoeuvre dans le nombres de mots. si Byte est trop restrictif, on peu le declarer en type word (0..65535) ou encore LongWord (0..beaucoup) ce qui augmenteras le nombre de mots possible dans une chaine.
Mais byte me semble tout a fait correct pour cette utilisation.
fonctionnement :
Dans le cas ou Posi est egal a 1 et que le separateur est en position 1 on sort tout de suite de la fonction (exit), vus qu'on a deja assigné '' a result mais copy serat capable de renvoyer un element vide a une autre position.
Dans le cas ou Posi est superieur au nombres de separateurs, ChainePos boucle les elements en repartant de 1.
donc en cas de depassement ChainePos revient au debut et renvois l'element correspondant.
exemple :
chat;chien;lapin;chevre
si on demande le mot 4, chainepos renvois "chevre",
si on demande l'element 5 (qui n'existe pas) chainepos renvois "chat" .
si on demande l'element 8 (qui n'existe pas non plus), chainepos renvois "chevre"
Grace a "posex(sep,dans,p1)+length(sep)" on peut definir des separateurs d'une longueur superieur a 1 caracteres. cela peut egalement etre trés utile si on utilise par exemple un Eoln ou double-Tabulation comme separateur (#13+#10 ou #9+#9).
ou encore, si le separateur est un mot constant lui aussi exemple :
"chienPOUFchatPOUFlapinPOUFcheval"
ici notre separateur serat "POUF"
18 déc. 2005 à 11:40
function GetTok(Chaine: String; Position: byte; Separateur: Char):String;
var
i:byte;
temp:string;
begin
{ initialise les variables }
i := 1;
temp := chaine;
{ si 1er token }
if position = 1 then begin
{ si la fin de la chaine est un separateur }
if pos(separateur,chaine) > 0 then begin
result := copy(chaine,1,pos(separateur,chaine)-1);
exit;
{ sinon }
end else begin
result := chaine;
exit;
end;
{ sinon }
end else begin
{ effacer le 1er token jusqu'a atteindre la position voulue }
{ utilisation d'une variable temp à la place de chaine }
while i < position do begin
delete(temp,1,pos(separateur,temp));
i := i + 1;
end;
{ si la fin de la chaine est un separateur }
if pos(separateur,temp) > 0 then begin
result := copy(temp,1,pos(separateur,temp)-1);
exit;
{ sinon }
end else begin
result := temp;
exit;
end;
end;
end;
14 déc. 2005 à 11:12
Foxi->Désolé de ne pas avoir envisagé des fichiers spéciaux comportant des séparateurs à plusieurs caractères ... Pour ce qui est de copier une fonction d'une unit, celà me semble extrêment intéressant et devrait faire l'objet d'un petit tutoriel de ta part sur les pièges à éviter.
pour les separateurs multicaractere je pense en premier lieu au fin de ligne (EOLN) dans un fichiers texte par exemple (#13+#10), chainepos peut permettre de renvoyer toute une ligne d'un tel fichier.
mais egalement pour les fichiers binaire, qui par exemple utilise un h0000 ou encore h00000000 pour separer certain element tel par exemple les IdTag des MP3 ect...
ce qui permet a ChainePos d'avoir une utilitée réellement plus vaste.
un exemple concret, on recupere dans un stream la partie du fichier binaire qui nous interresse, et un simple apel a ChainePos nous permet de recuperer directement ce qui nous interresse et la mon astuce "const posi : cardinal = 1" vas nous servir.
Avec READ, on recupere un Char ou un tableau ... string n'est rien d'autre qu'un type PChar "humanisé".
admettons que le stream contient plusieurs partie avec données a taille variable et separateurs egalement variable.
admettons :
SPart (pchar) = ABCD #0#0 EF #0#0#0#0 GHI #2 JKLM #0#0 NOP #32#32 QRS
recuperons les elements grace a ChainePos :
ABCD = ChainePos(#0+#0, SPart)
EF = ChainePos(#0+#0, SPart, 2)
GHI = ChainePos(#0+#0+#0+#0, SPart, 2)
JKLM = ChainePos(#2, SPart, 2)
NOP = ChainePos(#32+#32, ChainePos(#0+#0, SPart, 5)) << 5 parce que #0#0#0#0 est consideré comme deux separateur #0#0 et #0#0 avec un element vide au milieu.
QRS = ChainePos(#32+#32, ChainePos(#0+#0, SPart, 5), 2) << idem que pour NOP
tout cela sans toucher au stream ou a la position de l'offset de lecture dans le stream.
vus les performances que j'ai obtenus avec ma version de ChainePos utilisé dans un Listbox pour transformer le texte affiché par rapport au texte contenus ... je pense que ça reste carrement correct (je rapel que 100000 requette a ma methode prennent environ 63ms en moyenne seulement).
bon la l'exemple est bidon... mais l'utilisation de chainepos de cette façon reste une possibilitée a prendre en compte.
par contre les autre proposition a separateur uni-caractere ne peuvent pas etendre leur utilisation jusque la.
donc comme l'a dit JLen100 a propos de ma methode, non seulement plus generale mais egalement a mon sens trés polyvalente. ce qui est forcement un plus car elle peut etre utilisée la ou on s'y attend le moins.
Tu prend notre ChainePos et Format et tu as quasiment plus besoin des autres fonctions de traitements de chaines de caractere. (qu'elle pretention! hihihi)
d'ailleur je m'etonne qu'une fonction aussi pratique que ChainePos n'existe pas dans delphi nativement. dans php il existe une fonction a peu prés equivalente GetWord() il me semble.
(vend methodes delphi pas cher ... bon etat general ... prix a negocier ... hehehe)
14 déc. 2005 à 10:35
pour le portage ce que tu dis est vrai tant que tu n'appelles pas de ressource du bios ou d'entrées sorties.
pour le timer je t'expliquerais ça en message perso vu que ce n'est pas le sujet ici (la solution c'est de travailler en asynchrone pour ne jamais attendre et de traiter l'information quand elle est complète).
mon propos c'était simplement de dire que si windows gére le mapping mémoire il peut être mis en défaut sur des problémes de timming (en gros et pour faire simple quand une tache est longue quand elle rend la main windows n'est plus capable d'exécuter celles qui se sont accumulées.
pour le systeme windows c'est Xp pro
14 déc. 2005 à 10:32
vus que l'interet apparement est de ne pas augmenter la taille de la close uses :
on extrait PosEx de strutils et on la place dans l'unité principale.
copy, pos et length sont incluse dans l'unité system (qui ne necessite pas qu'on la declare)
donc avec ma methode on obtient certe deux fonctions au lieu d'une (posex et chainepos)
tout en gardant les perf et les avantages de ma version.
alors revoila une version "compressée non lisible" de la fonction (sans avoir a declarer une unité en plus dans les uses) :
function ChainePos(const Sep, Dans: String; const posi: cardinal = 1): String;
function e_PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer;
var I, X, 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;
var i,p1,p2 : integer;
begin
result :''; if (Dans[1] Sep) and (posi = 1) then exit;
p1 := 1; for i := 1 to posi-1 do begin
p1 :e_posex(sep,dans,p1); if p1 0 then exit else p1 := p1 + length(sep); end;
p2 :e_posex(sep,dans,p1); if p2 0 then p2 := length(Dans)+1;
result := copy(dans,p1,p2-p1);
end;
(je tiens a la vendre cette methode ou quoi ^^ lol)
14 déc. 2005 à 09:59
Heu ... l'assembleur c'est la base de toute machine, je ne vois pas pourquoi Windows n'accepterais pas trop cela vus que tout programme compiler est forcement retranscrit en assembleur.
puis l'assembleur est relativement bien portable vus qu'on agit directement sur le Proco.
bien entendus, il faudrat tenir compte du type de CPU et du jeux d'instruc supporté par le CPU... le seul probleme qu'il y auras serat sur un portage asmX86 vers Motorola et autre.
pour ce qui est des boucles, forcement si tu fait pas gaffe a la sortie ... que ça soit en n'importe quel langage ça reviendras a faire freezer l'appli ce qui est toujours mauvais.
pour le truc du timer qui attend ... la je dirais, mauvaise methode! il faut utiliser les evenements plutot qu'un timer (bien que je ne connaisse pas réellement le probleme exposé).
mais un timer pour ça je dirais = ressource gachée inutilement.
et puis je vois mal quel dev aurait envie de créer une boucle infinie aucun interet de developper un bugware, windows suffit.
question, sous quel systeme windows est tu ?
13 déc. 2005 à 11:13
autre exemple: attendre dans un timer une chaine de caractères sur la liaison série rend windows instable.( au mieux ce sont des problèmes d'affichage au pire le plantage)
enfin en assembleur rien de plus facile que de faire un infinit loop sans erreur d'adressage et là windows n'y voit goute!!
13 déc. 2005 à 10:58
13 déc. 2005 à 10:47
--> AMEHDOUDALI pour le tuto ça a été un peu laborieux : je ne devrais jamais programmer avant et après une teuf!!
pour l'utilisation des unités il me semblait qu'à la compilation DELPHI n'intégrait que les fonctions et procédures utilisées et que les autres étaient ignorées. (c'est ce que faisait Turbo Pascal)
13 déc. 2005 à 10:20
13 déc. 2005 à 10:10
Jlen100->Je remercie pour l'excellent tuto sur les lecture/ecriture de bits (voir forum).
Foxi->Désolé de ne pas avoir envisagé des fichiers spéciaux comportant des séparateurs à plusieurs caractères (Champ1CeciestseparateuroptimisestandardChamp2Ceciestseparateuroptimisestandard !). Pour ce qui est de copier une fonction d'une unit, celà me semble extrêment intéressant et devrait faire l'objet d'un petit tutoriel de ta part sur les pièges à éviter. Pour ma part, je teste cette éventualité.
12 déc. 2005 à 18:33
@JLen100 : les performances sont fortement degradée a partir du moment ou on utilise ceci :
for x := S to F do string := string + char[x];
constatation generale... no comment. c'est lourd. et le seul moyen de gagner en perf et d'utiliser les pointeurs, ce qui bien sur (et je ne comprend toujours pas pourquoi) n'est pas conseiller avec Dot Net.
@Ahmed : afin de ne pas accroitre les declaration uses (d'ailleur je vois pas ou est le probleme) on peu extraire la fonction Posex() de l'unité StrUtils.
function PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer;
var I, X, 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;
Copy et Length sont incluse dans l'unité System qui est presque "obligatoire" la plupart du temps. donc ça ne changeras rien de les extraires de cette derniere.
On oblige a utiliser un nombre positif car il serait illogique de demander des elements en dehors du champs 1..N. tout simplement.
Pour ta fonction, et meme certaines autres propositions, il y a soit un probleme de performance, soit un probleme de souplesse (separateur a caractere unique)...
DelphiProg a bien cerné l'utilisation plus generale de ma fonction. si maintenant cette fonction ne vas servir que dans ce programme pour une utilisation bien precise, voir les autres solutions proposées.
Pour ton soucis d'augmenter les declarations dans la close Uses, tu peut toujours te créer une unité d'alias, cela reviendras au meme mais diminueras la close principale ^^
@Emhandal : les labels sont assé vieux, mais depuis que le pascal n'est plus un langage lineaire ils n'ont plus raisons d'etre. bon ça peut toujours etre utile dans certain cas rare, mais un debutant devrais apprendre a programmer en delphi sans les utiliser.
tiré de l'aide delphi :
L'utilisation de l'instruction goto est généralement déconseillée en programmation structurée. Elle peut néanmoins être utilisée dans certains cas pour sortir de boucles imbriquées.
11 déc. 2005 à 13:18
11 déc. 2005 à 13:09
De plus c'est par octets, le Dans[0] serait valable que dans un ShortString (qui se limite qu'à 255 caractères). Pour les string il faut prendre Dans[-3]..Dans[0] vu que la chaine peut aller jusqu'a High(Integer)...
11 déc. 2005 à 13:04
11 déc. 2005 à 12:55
11 déc. 2005 à 12:19
11 déc. 2005 à 11:57
11 déc. 2005 à 11:54
11 déc. 2005 à 11:48
*J'ai maintenu b pour une lecture aisée
Pour Tout le monde
*Je cherche un moyen d'éviter les fonctions et donc m'approcher d'une version automate(reste length()). Une idée ?
D'avance merci.
11 déc. 2005 à 11:31
11 déc. 2005 à 11:21
plutot que de faire:
if (b=Sep)then mets plutot if (Dans[i]=Sep)then...
et utilises un index de début et un index de fin et avant de sortir tu fais:
result:= copy(Dans,debut,fin-debut) tu éviteras les réallocations de mémoire.
nota copy et length(que tu utilises) font parties de l'unité system.
11 déc. 2005 à 10:47
Je dois convenir que vos remarques sont empreintes d'une maîtrise que j'espère atteindre.
*La petite histoire de ce code (en dehors de ressusciter le basic) est un pari avec un collègue d'arriver à un automate, avec le moins de lignes (il en a horreur) et à traiter tous les cas de figure possibles sur une ligne d'un fichier texte délimité :
--> Ahmed/Boudali/ ==>si 1 alors Ahmed,si 3 alors vide
--> /Ahmed/Boudali/ ==>si 1 alors vide,si 2 alors Ahmed
--> /Ahmed//Boudali/ ==>si 1 alors vide,si 3 alors vide
--> /Ahmed/Boudali ==>si 1 alors vide,si 4 alors vide
*L'utilisation de length(), posex(), copy() oblige à accroître la liste "Uses".
*Pourquoi forcer l'entrée d'un numéro de position positif (format byte ou word) ?
*Reste la performance et les allocations de mémoire pour string, là mon code pêche... Effectivement, les variables "Sep" et "b" doivent être déclaré en char, la variable "a" doit être remplacé par Result et Result a une longueur maximum équivalent à celle de la valeur entrée ("Dans").
Merci encore pour ces pertinentes remarques.
10 déc. 2005 à 19:52
Ca commençait à nous manquer ces derniers temps, n'est-ce pas foxi ? ;o)
J'attire votre attention tout de même sur le fait que l'usage des pointeurs n'est pas bienvenu sous .Net. Si vous devez porter votre code pour qu'il fonctionne dans cet environnement, vous devrez alors privilégier la solution proposée par foxi.
10 déc. 2005 à 18:27
deux petites remarques
- la chaine ne doit pas contenir de #0 ( Pchar à 0 terminal)
- la routine de foxi est plus générale ( pas de limitation sur la longueur du séparateur)
mais à part ces détails joli travail!!
@+
jlen
10 déc. 2005 à 18:17
10 déc. 2005 à 18:06
ma version delphi :
Function SubString(aStr: String; aSepar: Char; n: Integer): String;
{===============================================================================}
{ renvoie ce qui est à gauche de la droite de la n ieme sous chaine séparée par }
{ aSepar de la chaine aStr }
{ ex : SubString('TMP|c:\rep1\reptmp\', '|', 1) renvoie 'c:\rep1\reptmp\' }
{ Permet d'extraire un à un les éléments d'une chaine séparés par un séparateur }
{===============================================================================}
Var
pStr, pRes, pStart: PChar;
Nb: Integer;
Function PosChr(aString: PChar; aChr: Char): PChar;
Begin
Result := nil;
While aString^<>#0 do
begin
If aString^=aChr Then
begin Result := aString; Exit; end;
Inc(aString);
end;
End;
Begin
Result := '';
If n<0 Then Exit;
aStr := aStr+aSepar;
Nb := 0;
pStr := PChar(aStr);
While pStr^<>#0 do
begin
pRes := PosChr(pStr, aSepar);
If pRes=nil Then Exit;
If Nb=n Then
begin System.SetString(Result, pStr, pRes-pStr); Exit; end;
pStr := pRes+1;
Inc(Nb);
end;
End;
c'est une vieille routine que j'ai adaptée pour gérer uniquement des caractères comme séparateur
f0xi -> tu serais étonné si tu voyais certains de mes codes... j'ai deja mis des goto qui font gagner en rapiditée dans mes codes :)
10 déc. 2005 à 07:57
pour le manque de performance du code je pense (mais je ne l'ai pas tester)que cela viens surtout des affectations des strings (a et b).
je m'explique quand on uilise un string en variable locale c'est toujours une variable dynamique donc a chaque fois qu'une modification de la variable affecte sa longueur le programme travaille comme ça:
appel au gestionnaire de tas pour reserver l'espace mémoire pour la taille de chaine
concatenetion de la chaine initiale et de l'ajout
libération de la mémoire occupée par la chaine initiale
il est donc préférable de considérer la chaine comme un tableau et travailler sur des pointeurs dans ce tableau (ce que fait posex)
c'est aussi pour cela que quand on le peut il vaut mieux affecter une longueur fixe à une chaine par l'utilisation de string[xx] le commpilateur reserve alors un espace mémoire fixe
il en est de même quand on déclare une variable de type scalaire (char, integer, byte......).
10 déc. 2005 à 01:36
alors pour le petit probleme, il n'est pas voulus. c'est en fait posex qui procede de cette maniere.
en effet, d'accord avec toi sur le fait que ce n'est pas trés "delphi".
donc voila un correctif qui ne degrade meme pas les performance de la routine :
function ChainePos(const Sep, Dans: String; const posi: byte = 1): String;
var i,p1,p2 : integer;
begin
result := '';
if (Dans[1] = Sep) and (posi = 1) then begin
exit;
end;
p1 := 1;
for i := 1 to posi-1 do begin
p1 := posex(sep,dans,p1);
// permet de stoper si on vas au dela de la taille de la chaine.
if p1 = 0 then begin
exit;
end
// sinon on continus.
else begin
p1 := p1 + length(sep);
end;
end;
p2 := posex(sep,dans,p1);
if p2 = 0 then p2 := length(Dans)+1;
result := copy(dans,p1,p2-p1);
end;
voila, conforme a l'esprit delphi.
9 déc. 2005 à 23:29
en cas de dépassement il serait plus judicieux de renvoyer une chaine vide (c'est plus conforme à l'esprit DELPHI et à la logique: si on est au dela c'est qu'on a pas trouver l'élément d'autre part si tu retournes le premier terme il y a ambiguité avec la requete du mot 1 ce qui ne facilite pas l'utilisation.
-->AhmedBoudali
plutot que d'utiliser un GOTO antediluvien tu aurais pu écrire:
if (Dans[1]=Sep) and (posi=1) then
begin
Result:=a;
exit;
end;
9 déc. 2005 à 23:06
tu peux faire comme cela :
uses strutils; //pour posex
function ChainePos(const Sep, Dans: String; const posi: byte = 1): String;
var i,p1,p2 : integer;
begin
result := '';
// si le premier element n'est pas definis on sort tout de suite
if (Dans[1] = Sep) and (posi = 1) then exit;
// on initialise P1 a 1
p1 := 1;
// on recherche la position du separateur N dans la chaine
for i := 1 to posi-1 do p1 := posex(sep,dans,p1)+length(sep);
// on recherche la position du prochain separateur aprés P1
p2 := posex(sep,dans,p1);
// si on l'a pas trouver c'est qu'on est surrement en fin de chaine
// P2 devient donc la taille de la chaine+1.
if p2 = 0 then p2 := length(Dans)+1;
// on renvois le resultat en utilisant la fonction copy
result := copy(Dans, p1, p2-p1);
end;
Cette methode est beaucoup plus rapide; 68ms de moyenne pour ma methode contre 1900ms de moyenne pour la tienne sur 100000 requettes. Les performances de tes programmes s'en trouverons donc grandement ameliorée.
La supression des variables A et B permettras aussi de gagner en memoire.
d'ailleur dans ta methode, B aurait du etre declarée en Char et non en String.
l'ecriture de "const posi : integer = 1" permet de ne pas definir posi si on desir le premier mot de la chaine. ce qui pourrait etre utile dans certaine utilisation.
d'ailleur sur ce point, posi doit etre de type Byte (0..255) pour eviter les nombre negatif.
cela laisse une large manoeuvre dans le nombres de mots. si Byte est trop restrictif, on peu le declarer en type word (0..65535) ou encore LongWord (0..beaucoup) ce qui augmenteras le nombre de mots possible dans une chaine.
Mais byte me semble tout a fait correct pour cette utilisation.
fonctionnement :
Dans le cas ou Posi est egal a 1 et que le separateur est en position 1 on sort tout de suite de la fonction (exit), vus qu'on a deja assigné '' a result mais copy serat capable de renvoyer un element vide a une autre position.
Dans le cas ou Posi est superieur au nombres de separateurs, ChainePos boucle les elements en repartant de 1.
donc en cas de depassement ChainePos revient au debut et renvois l'element correspondant.
exemple :
chat;chien;lapin;chevre
si on demande le mot 4, chainepos renvois "chevre",
si on demande l'element 5 (qui n'existe pas) chainepos renvois "chat" .
si on demande l'element 8 (qui n'existe pas non plus), chainepos renvois "chevre"
Grace a "posex(sep,dans,p1)+length(sep)" on peut definir des separateurs d'une longueur superieur a 1 caracteres. cela peut egalement etre trés utile si on utilise par exemple un Eoln ou double-Tabulation comme separateur (#13+#10 ou #9+#9).
ou encore, si le separateur est un mot constant lui aussi exemple :
"chienPOUFchatPOUFlapinPOUFcheval"
ici notre separateur serat "POUF"
voila, j'espere que tout cela te serviras.