FONCTION QUI SUPPRIME UN CARACTÈRE DONNÉ DANS UNE CHAINE EN UTILISANT LES POINTE

Utilisateur anonyme - 7 févr. 2006 à 23:36
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 - 28 mai 2006 à 17:23
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/35940-fonction-qui-supprime-un-caractere-donne-dans-une-chaine-en-utilisant-les-pointeurs

cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
28 mai 2006 à 17:23
Je n'étais pas encore venu foutre ma m**** lol
En mettant le caractère #0 au milieu d'une chaîne, fOxi, tu ne changes pas sa longueur. Exemple:

const s=#1#2#0#3#4;

Avec cet exemple de chaîne qui fait 5 caractères, le compilateur te dira que Length(s)=5 (la convention qu'un chaîne se termine au premier caractère #0 est vraie - et en partie seulement - pour le C mais pas en Delphi). De plus, même en C le fait qu'un char* contienne un \0 à un endroit ne signifie pas qu'il n'y a pas encore des données après, c'est juste une convention commode qu'utilisent ceux qui écrivent les API pour s'y retrouver.

Pour Delphi, une chaîne de type string est implémentée par une sorte d'interface sous-jacente (pour le ref-counting) avec un champ Length. C'est aussi le cas des tableaux dynamiques. C'est par exemple pour ça que si tu as:

var s:string;

@s ne pointe pas vers le premier caractère de la chaîne, mais vers cette fameuse interface de gestion des chaînes (idem pour les tableaux dynamiques). Tu peux calculer la taille en mémoire occupée par ce que Borland rajoutte (en plus des caractères) à une chaîne en calculant:

Integer(@s[1])-Integer(@s)

Je crois (mais ça demanderait à être revérifié) que ça fait 8 octets (tout simplement un entier integer contenant la longueur de la chaîne + un autre contenant le nombre de références à celle-ci).

Une version encore plus compacte de la fonction de Fred serait:

function SuppChar(const S : string; const C : char) : string;
var pS, pR : PChar; l : Integer;
begin
l := length(S)
SetLength(Result, l);
pS := PChar(S);
pR := PChar(Result);
While pS^ <> #0 do begin
if pS[0] <> C then begin
pR[0] := pS[0];
Inc(pR);
end else
Dec(l);
Inc(pS);
end;
SetLength(Result, l);
end;

Je ne pense pas que le Dec(l) soit trop gourmand en resources ^^

Dernière remarque, tu peux t'affranchir de faire Result:='' au début de la fonction car Delphi par défaut le fait pour toi (comme il le fait pour toutes les variables qui contiennent des interfaces "ref-counted").
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
16 avril 2006 à 15:57
en effet fred ...

alors tout simplement le rajout de :

function SuppChar(const S : string; const C : char) : string;
var pS, pR : PChar;
begin
Result := '';
SetLength(Result, length(S));
pS := PChar(S);
pR := PChar(Result);
While pS^ <> #0 do begin
if pS[0] <> C then begin
pR[0] := pS[0];
Inc(pR);
end;
Inc(pS);
end;
pR[0] := #0;
end;

et hop la chaine est finie...
Fred_Ca_Pulse Messages postés 20 Date d'inscription mercredi 15 mars 2006 Statut Membre Dernière intervention 19 juin 2008
15 mars 2006 à 11:02
Mais non, c'est seulement une blague, bien sûr qu'elle n'existe pas depuis 50 ans ... j'étais même pas né à l'époque ... et le Pascal non plus effectivement :)

C'était juste pour faire un peu d'ironie sur le temps passé à écrire ou à utiliser du code, et la possibilité qu'on puisse toujours avoir des bugs dedans ou qu'il existe une meilleure façon de l'écrire.

FRED
jlen100 Messages postés 1606 Date d'inscription samedi 10 juillet 2004 Statut Membre Dernière intervention 25 juillet 2014 13
15 mars 2006 à 10:16
désolé pour le triple post mais le seveur m'a renvoyer 2 fois une erreur de page!!
@+
jlen
jlen100 Messages postés 1606 Date d'inscription samedi 10 juillet 2004 Statut Membre Dernière intervention 25 juillet 2014 13
15 mars 2006 à 10:14
Fred_ca_Pulse---> faut pas pousser le bouchon trop loin : quand tu dis que ta fonction existe depuis 50 ans au moins elle est donc antérieure à l'invention du Pascal (en 1971 je cois) et de l'introduction de la notion de pointeur dans ce langage (debut des années 80 si ma mémoire est bonne) Très fort!!!

@+
jlen
Fred_Ca_Pulse Messages postés 20 Date d'inscription mercredi 15 mars 2006 Statut Membre Dernière intervention 19 juin 2008
15 mars 2006 à 10:04
Bonjour à tous d'un nouveau sur le site !

Un petit commentaire pour f0xi du 12/02/2006.
Elle est effectivement très rapide ta fonction, mais elle marche pas bien, en effet la taille de destination va certainement changée et on obtient alors une chaîne dont la fin est non définie... pas très propre çà :)

Sur un modèle similaire j'ai écris :

function CaractereSupprimer( CSuppr : Char; const SChaine : String ) : String;
var
ILong, ILongInit : Longint;
PSource, PDest : PChar;
begin
ILong := Length( SChaine );
ILongInit := ILong;
SetLength( Result, ILong );
PSource := Pointer( SChaine );
PDest := Pointer( Result );
while ILong <> 0 do begin
if PSource^ <> CSuppr then
begin
PDest^ := PSource^;
Inc( PDest );
end;
Inc( PSource );
Dec( ILong );
end;
ILong := PDest - Pointer( Result );
if ILong <> ILongInit
then SetLength( Result, ILong );
end;

Ca fonctionne pour moi très bien depuis longtemps ... au moins 50 ans, c'est dire !!!
Mais je suis prêt à tout remettre en question pour une meilleure version...
... c'est comme çà qu'on avance !!!
walid2876 Messages postés 49 Date d'inscription mardi 26 avril 2005 Statut Membre Dernière intervention 31 août 2008
14 févr. 2006 à 22:23
Salut Mon frere Medilias
D'abord il faut respecter les commentaires des membres car ils vous montre la bonne voix pour y aller vite et surement.
meme si tu dis que tu as 15 avec pascal ton code ne le dit pas
avec (tout mon respect)il faut toujours apprendre
ne viens pas en 2006 pour dire au gens comment utiliser une liste
enchainée dynamiquement ()!!!!!
et tu as choisis le pire exemple avec malheur ..
je te souhaite une bonne experience en programmation et profite des conseils des membres surtout qui vienne de la part de NONO et autres et malgré que je vis avec le pascal depuis la version 3 et j'ai devellopé des dizaine de Logiciel proffesionnel je telecharge tous les exemples du nono et d'autre pour apprendre plus et je dis toujours que j'ai pas engendré 50% du savoir programmer
jlen100 Messages postés 1606 Date d'inscription samedi 10 juillet 2004 Statut Membre Dernière intervention 25 juillet 2014 13
12 févr. 2006 à 18:35
salut,
foxi-->le principe des listes chainées est le suivant:
on decrit dans un record la structure de l'enregistrement auquel on ajoute un pointeur sur l'enregistrement suivant (dans le cas des listes doublement chainée on rajoute an plus un pointeur sur l'enregistrement précédent) si ce pointeur est à Nil on se trouve sur le dernier enregistrement .Le compilateur lui se moque totalemnt de savoir ce qu'il y a dedans,(pour lui c'est simplement un espace mémoire de 4 octets) ici il doit etre simplement du même type que l'enregistrement.( on pourrait même le déclarer, je pense, de type pointer.)
medelias-->quand je te dis que tu ne respectes pas les regles de base d'utilisation des pointeurs ce n'est pas innocent: une régle fondamentale est de toujours libérer la mémoire après utilisation des pointeurs en effet delphi ne le fait pas automatiquement
et tu te retrouves avec des fuites mémoires.
quand je dis qu'il n'est pas judicieux d'utiliser un pointeur pour stocker un caractére c'est déjà au niveau utilisation mémoire puisqu'il te faut mobiliser 5 octets pour 1 utile, ensuite en temps de process et d'occupation CPU : il faut appeler les routines de gestion de tas pour attribuer l'espace mémoire (et cela pour chaque caracteres)puis transférer le caractére et assigner le poinetur et si tu avais liberé la memoire les temps de traitement auraient certainement été multipliés par ~2.
je te rapelle également qu'une chaine de caracteres est un tableau et que l'on peut y acceder en donnant l'indice; en fait tu dis au programme d'aller chercher l'octet à l'adresse du tableau + l'offset (indice ou position du caractere) dans ce cas tu utilises implicitement un pointeur et en plus tu fais un transtypage. Notes également que c'est un excellent moyen pour passer des valeurs numériques dans une chaine de caracteres sans faire convertion.
Je dis également que ton exemple est mal choisi pour expliquer l'utilisation des listes chainées puisque tu ne montres ni comment on insère un élément dans la liste ni comment on le retire.(sans parler de la mauvaise utilisation des pointeurs)

Enfin j'ajouterai que l'utilisation des listes chainées est un reste du TP et qu'il vaut bien mieux , en général, lui préférer celle des Tlist qui bénéficient des methodes d'ajout et de suppression déjà implémentée , plus quelques propriétés fort utile.

je ne veux pas donner de leçons mais j'aimerais que ceux qui donnent le fasse à bon escient

@+
jlen
medelias Messages postés 18 Date d'inscription mardi 28 octobre 2003 Statut Membre Dernière intervention 12 mars 2006
12 févr. 2006 à 15:10
Salut;

je sais qu'il y a des O.O.M en executant cette fonction sur des textes car il y aura trop de new que de dispose mais comme j'avais déja mentionner c'est juste un exemple pour comprendre un petit peu le fonctionnement des pointeurs.

je vous prometterai tous un très beau logiciel à l'avenir.

P.S. désolé tous pour la mauvaise humeur.

CHAO
Utilisateur anonyme
12 févr. 2006 à 13:49
Salut,
Medelias je reprend tes propos :"le seul défaut c'est qu'il n'y avait pas de commentaires." où tu es de mauvaise foi où tu n'as pas lu les commentaires de f0xi
il sont pourtant claire et sans appel :
-Fuites de mémoire en Méga Octets voir même Out Of Memory
-Et le temps d'execution qui est au moins 20 fois plus long
Et toi la seul chose que tu as retenu c'est le manque de commentaires ???? :-(

@+
Cirec
medelias Messages postés 18 Date d'inscription mardi 28 octobre 2003 Statut Membre Dernière intervention 12 mars 2006
12 févr. 2006 à 11:06
salut à vous tous,

delphi compiles ça :
type PReference = ^TReference;
TReference = record
Valeur : Char;
Svt : PReference;
end;
pour créer une liste chainée.

j'ai écris cette fonction pour des étudiants qui débuttent en pascal et surtout en pointeur et pour comprendre aussi la création d'une liste chainée. je ne vais pas utiliser quand même une telle fonction dans delphi là où il y a StringReplace, c'était juste à titre éducatif, le seul défaut c'est qu'il n'y avait pas de commentaires.

Chao
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
12 févr. 2006 à 09:11
de plus, je ne comprend pas que delphi puisse compiler cela :


type PReference = ^TReference;
TReference = record
Valeur : Char;
Svt : PReference;
end;

Svt pointeur sur record TReference qui contient lui meme un pointeur Svt pointeur sur record TReference qui contient egalement Svt pointeur sur record TReference qui contient lui aussi Svt pointeur sur record TReference qui contient encore une fois Svt pointeur sur record TReference qui contient a son tours Svt pointeur sur record TReference qui contient .......

je comprend maintenant d'ou viennent les 400Mo de charge.
et je ne pense pas que des en plus dipsose() y changeront quelque chose.

Mais bon voyons le bon coté des choses, c'est censé etre un exemple ? ok pas de problemes.
c'est bien un exemple de ce qu'il ne faut pas faire.
quelque part ... le defi est reussi. (sincerement)

errare humanum est, perseverare diabolicum,
cuiusvis hominis est errare, omnis homo perseverare. Sic.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
12 févr. 2006 à 08:34
alors ...

premierement le fait de passer REF en parametre VAR c'est assé contraignant car on ne peu pas passer la valeur d'un edit ou label par exemple directement.
on doit créer une locale ou une globale pour traiter la chaine, ce qui est loin d'etre parfait comme utilisation.

en fait c'est un peu illogique meme de modifier la chaine envoyée et de renvoyer la chaine modifier en resultat de fonction ... autant travailler directement sur le resultat de la fonction car comme on l'a souvent dit, l'appel a result ne provoque pas de sortie prematurée de la fonction comme ne C/C++ ou autre langage.

moi j'aurais ecrit la fonction de cette façon (en remerciant florenth au passage pour m'avoir montrer comment utiliser les pointeurs)

-----

function SuppChar(const S : string; const C : char) : string;
var pS, pR : PChar;
begin
Result := '';
SetLength(Result, length(S));
pS := PChar(S);
pR := PChar(Result);
While pS^ <> #0 do begin
if pS[0] <> C then begin
pR[0] := pS[0];
Inc(pR);
end;
Inc(pS);
end;
end;

----

sur les performances y'a pas photo :

500000 requettes sur une chaine de 60 caracteres :
ma fonction : 1219ms (1.2 secondes)
StringReplace : 5328ms (5.3 secondes)
ta fonction : 22203ms (22.2 secondes)

50000 requettes sur une chaine de 255 caracteres :
ma fonction : 1641ms (1.6 secondes)
StringReplace : 22578ms (22.6 secondes)
ta fonction : Out Of Memory!

et encore je ne pousse pas le vice a donner la charge/fuite memoires relevées ...

aller si pourquoi pas ...

dans le premier test :
ma fonction : < 4 Ko
StringReplace : < 8 Ko
ta fonction : > 315Mo !

dans le deuxieme test :
ma fonction : < 8 Ko
StringReplace : < 10 Ko
ta fonction : dernier relevé a 453Mo avant OOM

sans vouloir critiquer ton travail ou parraitre medisant ou agressif, tes fonctions fonctionnent t'elles toujours aussi bien ? ou est-ce simplement l'oublis de faire un petit Bench (et un debugage) ?

La au moins ... c'est la preuve par les chiffres ...
une fonction qui bouffe 300 a 400Mo de memoire en quelque secondes ...
meme Quake 4 ne fait pas ça sur mon PC (quake 4 = 268Mo utilisé) et il fait bien plus de chose que de supprimer certains caracteres d'une chaine.

La remise en question n'est pas un echec, au contraire ça permet d'aller de l'avant.

Bref ... je note 0 car elle n'est pas fonctionnelle et pas du tout commentée.
l'effort et la ... le reste malheureusement ne suis pas.

ps : je suis developpeur amateur depuis 11 ans sur pascal et 5 sur delphi.
elguevel Messages postés 718 Date d'inscription jeudi 19 décembre 2002 Statut Membre Dernière intervention 22 novembre 2016 3
11 févr. 2006 à 10:04
Je trouve MEDIELAS assez nerveux, surtout que son code n'a pas été critiqué, on a juste dit qu'il existait une autre fonction plus simple.

Par contre sans entrer dans le code et sans méchanté (juré !) juste en observant le protoype :

function TForm1.SuppChar(var Ref: String; Ch: Char):String;

Tu laisse TForm1 ... je trouve pas çà propre pour qqn qui developpe depuis 15 ans et qui propose une fonction qui a pour but d'être intégré et utiliser tout de suite. Cela prouve aussi que tu as laissé ta fonction dans la classe de la page principal et non dans une unité ou une classe séparé. Donc maintenance et modularité = pas bon.

Enfin voila ... je m'arrette juste à cette ligne, mais je trouve que tu prend assez mal la critique.

++
medelias Messages postés 18 Date d'inscription mardi 28 octobre 2003 Statut Membre Dernière intervention 12 mars 2006
8 févr. 2006 à 13:33
Salut JLEN100,

j'ai vraiment adoré votre commentaire ça m'a fait penser à Matrix Revolution, pour que la communauté soit nombreuse je vous mettrez comme paramètre dans la fonction et puis dans un Repeat until False;

Merci à vous tous.
jlen100 Messages postés 1606 Date d'inscription samedi 10 juillet 2004 Statut Membre Dernière intervention 25 juillet 2014 13
8 févr. 2006 à 11:06
si tu n'acceptes pas les critiques c'est ton problème.

Maintenant comme ton code n'est pas un modèle de clareté médite sur cette maxime:

" Ce qui se conçoit aisément s'exprime facilement"

quand à me mettre dans ta fonction tu ne fairais que me multiplier!!

@+
jlen
medelias Messages postés 18 Date d'inscription mardi 28 octobre 2003 Statut Membre Dernière intervention 12 mars 2006
8 févr. 2006 à 10:26
Salut Nono40, et merci pour la remarque;

Ecoute JLEN100, je crois que c'est toi que je dois mettre dans la fonction et après ta décomposition je met en Free tout les variables et en Dispose tout les pointeurs et j'etteind mon PC

soit un peu gentil en donnant des conseils

a+ Nono40
cs_Nono40 Messages postés 962 Date d'inscription mercredi 3 avril 2002 Statut Membre Dernière intervention 12 septembre 2006 2
8 févr. 2006 à 02:01
En plus il manque beaucoup de dispose() par rapport au nombre de new(). Grosse fuites de mémoires en perspective.
Niveau optimisation ce n'est pas terrible dans tous les cas.
( ca fait 16 ans que je bosse en pascal de TP3 à DElphi 2006 )
medelias Messages postés 18 Date d'inscription mardi 28 octobre 2003 Statut Membre Dernière intervention 12 mars 2006
8 févr. 2006 à 00:53
Hey les gars

mon but c'était de montrer comment travailler avec les pointeurs, je ne dis pas que je connais toutes les fonctions de Delphi mais à 90% donc n'essaye pas de m'exciter la prochaine fois, on est pas là pour donner des leçon de moral. ça fait plus de 15 ans que je developpe avec Pascal Objet et Delphi, so Shut uuuuuuuuuuuup
la prochaine fois essaye le mot MERCI, ça ne tues pas.
Utilisateur anonyme
7 févr. 2006 à 23:44
Désolé mais je ne voulais pas enfoncer le clou trop profondément
Et comme dirait l'autre :
À chacun mon tour ^_^
@+
Cirec
jlen100 Messages postés 1606 Date d'inscription samedi 10 juillet 2004 Statut Membre Dernière intervention 25 juillet 2014 13
7 févr. 2006 à 23:41
cirec tu m'as coupé l'herbe sous le pied!!
cela ne fait même qu'une seule ligne
@+
jlen
Utilisateur anonyme
7 févr. 2006 à 23:39
ps : il manque un bout :
Et en plus tu te compliques la vie on peut faire la même chose en deux lignes de code,
certe pas avec les pointeurs mais bon

@+
Cirec
Utilisateur anonyme
7 févr. 2006 à 23:36
Salut,
Manifestement tu ne connais pas la fonction StringReplace de SysUtils :

Function StringReplace(const S, OldPattern, NewPattern: string;
Flags: TReplaceFlags): string;

Et en plus tu te compliques la vie on peut faire la même chose en deux lignes de code

@+
Cirec