Remplacer un caractère dans une chaîne [Résolu]

Fred_Ca_Pulse 22 Messages postés mercredi 15 mars 2006Date d'inscription 19 juin 2008 Dernière intervention - 16 août 2006 à 09:44 - Dernière réponse : Fred_Ca_Pulse 22 Messages postés mercredi 15 mars 2006Date d'inscription 19 juin 2008 Dernière intervention
- 17 août 2006 à 11:05
Bonjour à tous

Pour otimiser mon code, j'ai voulu transformer en procédure la fonction de remplacement que j'utilise depuis longtemps déjà :

function CaractereRemplacer( CSource, CDestination : Char; const SChaine : String ) : String;
var
   ILong : Longint;
   Source, Dest : PChar;
begin
     ILong := Length( SChaine );
     SetLength( Result, ILong );
     Source := Pointer( SChaine );
     Dest := Pointer( Result );
     while ILong <> 0 do begin
           if Source^ = CSource
              then Dest^ := CDestination
              else Dest^ := Source^;
           Inc( Source );
           Inc( Dest );
           Dec( ILong );
     end;
end;


Son équivalent en procédure me semblait assez évident :

procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
   ILong : Longint;
   Source : PChar;
begin
     ILong := Length( SChaine );
     Source := Pointer( SChaine );
     while ILong <> 0 do begin
           if Source^ = CSource
              then Source^ := CDestination;
           Inc( Source );
           Dec( ILong );
     end;
end;

Mais, bien sûr , çà ne marche pas comme je voudrais  :
 j'obtiens une merveilleuse "Violation d'accès" lorqu'un caractère doit être remplacé par Source^ := CDestination

J'ai bien pensé à un problème de référencement des chaînes et j'ai donc ajouté la ligne
"UniqueString( SChaine );" en début de procédure :
çà fonctionne alors mais les performances ne sont plus à la hauteur !

Si vous avez une petite îdée sur la question, merci de m'en faire part.
Afficher la suite 

Votre réponse

25 réponses

Meilleure réponse
Fred_Ca_Pulse 22 Messages postés mercredi 15 mars 2006Date d'inscription 19 juin 2008 Dernière intervention - 16 août 2006 à 11:24
3
Merci
Effectivement Jinh a raison :
- sa procédure marche
- le "@SChaine[1]" doit bien être équivalent à "UniqueString" : la vitesse d'exécution est la même

Cependant cette vitesse ne semble pas pouvoir être meilleure que celle de la version fonction :
- environ 10% moins rapide sur mes tests
- dans les 2 cas la chaîne résultante doit être allouée : mais en fonction la copie des caractères ne nécessite qu'une passe au lieu de 2

Il semble que le problème est le suivant :
- soit le cas simple
STest1 := 'toto';
STest2 := STest1; // Poiteur sur la même zone mémoire pour le moment !
CaractereRemplacerP( 'o', 'a', STest2 );
- si la procédure faisait le remplacement directement (sans UniqueString) 
. on aurait au final : STest1 = STest2 = 'tata'
. c'est pas vraiment le but car il ne faut pas modifier STest1
- Delphi est donc très fort (qui en doutait !) : il a raison de planter
- on n'optimise donc rien en transformant la fonction en procédure

Merci Fred_Ca_Pulse 3

codes-sources a aidé 80 internautes ce mois-ci

Commenter la réponse de Fred_Ca_Pulse
Meilleure réponse
jinh68 215 Messages postés mardi 29 juillet 2003Date d'inscription 1 septembre 2006 Dernière intervention - 16 août 2006 à 11:30
3
Merci
Très beau bilan ;)!


Je pense qu'en réalité, tu obtiendrais les meilleurs gain en performance en passant par l'asm(sur phidels.com, tu trouveras quelques posts traitant de ce sujet si cela t'intéresse ;)).

j!nH

Merci jinh68 3

codes-sources a aidé 80 internautes ce mois-ci

Commenter la réponse de jinh68
DRJEROME 447 Messages postés jeudi 9 janvier 2003Date d'inscription 5 février 2015 Dernière intervention - 16 août 2006 à 10:04
0
Merci
tu veux optimiser la fonction stringreplace si je comprends bien ?

DrJerome
Commenter la réponse de DRJEROME
Fred_Ca_Pulse 22 Messages postés mercredi 15 mars 2006Date d'inscription 19 juin 2008 Dernière intervention - 16 août 2006 à 10:26
0
Merci
Oui, en quelque sorte, car ma fonction correspond plutot à un "CharReplace".
La fonction marche très bien et est environ 12 fois plus rapide que son équivalent effectué par stringreplace.
En procédure elle devrait être plus rapide encore ... si çà marchait.
Commenter la réponse de Fred_Ca_Pulse
jinh68 215 Messages postés mardi 29 juillet 2003Date d'inscription 1 septembre 2006 Dernière intervention - 16 août 2006 à 10:43
0
Merci
Salut,

L'adresse du  premier caractère d'une string Delphi n'est pas équivalent à l'adresse du string elle-même(préambule de 8 octets: compteur de référence + longueur).

Cette version marche, dis nous si les performances sont meilleures(même si je pense que l'appel à @SChaine[1] revient à faire une UniqueString(sTaChaine), puisque cela force à placer l'adresse sur le tas).

procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
   ILong : Longint;
   Source : PChar;
begin
     ILong := Length( SChaine );
     Source := Pointer( @SChaine[1] );
     while ILong <> 0 do begin
           if Source^ = CSource
              then Source^ := CDestination;
           Inc( Source );
           Dec( ILong );
     end;
end;

j!nH
Commenter la réponse de jinh68
DRJEROME 447 Messages postés jeudi 9 janvier 2003Date d'inscription 5 février 2015 Dernière intervention - 16 août 2006 à 10:49
0
Merci
salut Jinh

ça m'a l'air excellent... en plus tu n'utilises pas de setlength : pour l'optimisation ça doit être bon ;)

DrJerome (ou JROD)
Commenter la réponse de DRJEROME
jinh68 215 Messages postés mardi 29 juillet 2003Date d'inscription 1 septembre 2006 Dernière intervention - 16 août 2006 à 10:52
0
Merci
Hey un phidéliste, ça fait toujours plaisir d'en croiser un ;) !

j!nH
Commenter la réponse de jinh68
Emandhal 199 Messages postés dimanche 2 mars 2003Date d'inscription 10 octobre 2006 Dernière intervention - 16 août 2006 à 10:57
0
Merci
Pourquoi ne pas remplacer Pointer( @SChaine[1] ); par PChar(SChaine); ?


Tout problème a sa solution... Mais en général, celle que l'on trouve n'est jamais la bonne...
Commenter la réponse de Emandhal
Emandhal 199 Messages postés dimanche 2 mars 2003Date d'inscription 10 octobre 2006 Dernière intervention - 16 août 2006 à 10:59
0
Merci
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
   Source : PChar;
begin
     Source := PChar( SChaine );
     while Source^<>#0 do
     begin
           if Source^ = CSource then Source^ := CDestination;
           Inc( Source );
     end;
end;

Tout problème a sa solution... Mais en général, celle que l'on trouve n'est jamais la bonne...
Commenter la réponse de Emandhal
jinh68 215 Messages postés mardi 29 juillet 2003Date d'inscription 1 septembre 2006 Dernière intervention - 16 août 2006 à 11:01
0
Merci
Apparemment cela plante, pour les raisons cités au-dessus je pense(adresse sur le tas).

j!nH
Commenter la réponse de jinh68
DRJEROME 447 Messages postés jeudi 9 janvier 2003Date d'inscription 5 février 2015 Dernière intervention - 16 août 2006 à 11:09
0
Merci
Emandhal voulait-il dire : 
<hr />
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
   Source : PChar;
begin
     Source := Pointer( @SChaine[1] );
     while Source^<>#0 do
     begin
           if Source^ = CSource then Source^ := CDestination;
           Inc( Source );
     end;
end;
<hr />

DrJerome
Commenter la réponse de DRJEROME
DRJEROME 447 Messages postés jeudi 9 janvier 2003Date d'inscription 5 février 2015 Dernière intervention - 16 août 2006 à 11:12
0
Merci
ou même :
<hr />
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
   Source : PChar;
begin
     Source := @SChaine[1];
     while Source^<>#0 do
     begin
           if Source^ = CSource then Source^ := CDestination;
           Inc( Source );
     end;
end;
<hr />

DrJerome
Commenter la réponse de DRJEROME
Emandhal 199 Messages postés dimanche 2 mars 2003Date d'inscription 10 octobre 2006 Dernière intervention - 16 août 2006 à 11:24
0
Merci
heuuu ui désolé, sinon la chaine est stockée dans le segment de code qui est protégé en écriture.

Tout problème a sa solution... Mais en général, celle que l'on trouve n'est jamais la bonne...
Commenter la réponse de Emandhal
DRJEROME 447 Messages postés jeudi 9 janvier 2003Date d'inscription 5 février 2015 Dernière intervention - 16 août 2006 à 11:26
0
Merci
c'est équivalent à
<hr />
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
   Source : PChar;
begin
     Source := @SChaine[1];
     while Source^<>^@ do
     begin
           if Source^ = CSource then Source^ := CDestination;
           Inc( Source );
     end;
end;
<hr />

DrJerome
Commenter la réponse de DRJEROME
DRJEROME 447 Messages postés jeudi 9 janvier 2003Date d'inscription 5 février 2015 Dernière intervention - 16 août 2006 à 11:30
0
Merci
oups on s'est croisé..


donc en résumé, la fonction c'est mieux que la procédure

DrJerome
Commenter la réponse de DRJEROME
Fred_Ca_Pulse 22 Messages postés mercredi 15 mars 2006Date d'inscription 19 juin 2008 Dernière intervention - 16 août 2006 à 11:39
0
Merci
L'asm doit peut-être donner une meilleure performance ... mais j'y connais rien du tout ... alors on va se contenter de la fonction :


function CaractereRemplacer( CSource, CDestination : Char; const SChaine : String ) : String;
var
   ILong : Longint;
   Source, Dest : PChar;
begin
     ILong := Length( SChaine );
     SetLength( Result, ILong );
     Source := Pointer( SChaine );
     Dest := Pointer( Result );
     while ILong <> 0 do begin
           if Source^ = CSource
              then Dest^ := CDestination
              else Dest^ := Source^;
           Inc( Source );
           Inc( Dest );
           Dec( ILong );
     end;
end;

Qui fonctionne bien. Merci à tous.
Commenter la réponse de Fred_Ca_Pulse
Cirec 4221 Messages postés vendredi 23 juillet 2004Date d'inscription 11 mai 2018 Dernière intervention - 16 août 2006 à 13:49
0
Merci
Salut,


si j'ai bien compris ta fonction est plus rapide que l'équvalent en procédure

Essaye celle-ci, en fait c'est exactement la même que la fonction mais en procédure

Procedure CaractereRemplacer( CSource, CDestination : Char; const SChaine : String; out result : String );
var
   ILong : Longint;
   Source, Dest : PChar;
begin
     ILong := Length( SChaine );
     SetLength( Result, ILong );
     Source := Pointer( SChaine );
     Dest := Pointer( Result );
     while ILong <> 0 dobegin
           if Source^ = CSource
              then Dest^ := CDestination
              else Dest^ : = Source^;
           Inc( Source );
           Inc( Dest );
           Dec( ILong );
     end;
end;






// Utilisation :


procedure TfrmMain.Button6Click(Sender: TObject);
Var Tmp : String;
begin
  CaractereRemplacer('e', 'i', 'Teste de remplacement de caractères', Tmp);
  Label1.Caption := Tmp;
end;

Dis moi ce que ça donne !














@+
Cirec
Commenter la réponse de Cirec
jinh68 215 Messages postés mardi 29 juillet 2003Date d'inscription 1 septembre 2006 Dernière intervention - 16 août 2006 à 14:29
0
Merci
Il y'aura un petit problème s'il passe Tmp en entrée et qu'il veut récupérer la même chaine en sortie ;).

j!nH
Commenter la réponse de jinh68
Fred_Ca_Pulse 22 Messages postés mercredi 15 mars 2006Date d'inscription 19 juin 2008 Dernière intervention - 16 août 2006 à 14:59
0
Merci
Effectivement Cirec, ta procédure ne marche pas dans le cas d'appels du type :

Tmp := 'Teste de remplacement de caractères';
CaractereRemplacer('e', 'i', Tmp, Tmp);

La variable de sortie est mise à vide.
Dommage car effectivement dans le cas que tu donne les performances sont meilleures que ma fonction initiale.

Par contre j'ai essayé :
procedure CaractereRemplacer( CSource, CDestination : Char; const SChaine : String; var Result : String );
var
   ILong : Longint;
   Source, Dest : PChar;
begin
     ILong := Length( SChaine );
     if Length( Result ) <> ILong
        then SetLength( Result, ILong ); // Réallocation si nécessaire
     Source := Pointer( SChaine );
     Dest := Pointer( Result );
     while ILong <> 0 do begin
           if Source^ = CSource
              then Dest^ := CDestination
              else Dest^ := Source^;
           Inc( Source );
           Inc( Dest );
           Dec( ILong );
     end;
end;

Sur un appel du type
CaractereRemplacer('e', 'i', Tmp, Tmp);
cela donne une superbe démonstration du même plantage que ma procédure initiale (celle sans UniqueString) :
'Violation d'accès'.
Et effectivement là çà me parait tout à fait logique : on veut écrire dans une chaîne constante et donc en lecture seule.

Qu'est-ce qu'ils sont durs à gober ces fichus pointeurs !
Commenter la réponse de Fred_Ca_Pulse
f0xi 4304 Messages postés samedi 16 octobre 2004Date d'inscription 9 mars 2018 Dernière intervention - 16 août 2006 à 16:14
0
Merci
28.3 .. 29.8 ms pour 100Kr

// S, cOld et cNew en const = -37..62 ms pour 100Kr
function ReplaceChar(const S : string; const cOld, cNew : Char) : String;
var
   pResult : PChar;
   i       : integer;
begin
     SetLength(Result,Length(S));
     // ne pas utiliser de PChar sur S = -8..14 ms pour 100Kr
     pResult := pChar(Result);
     i := 1;
     // repeat plus rapide que while = -10..40 ms pour 100Kr
     repeat
        // assignation directe du caractere = -1..3 ms pour 100Kr
        pResult[0] := S[i];
        if pResult[0] = cOld then
           pResult[0] := cNew;
        inc(pResult);
        inc(i);
     until S[i] = #0;
end;

Commenter la réponse de f0xi

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.

Remplacer un caractère dans une chaîne - page 2