Page 1 sur 2
Règle des signes
Posté : mar. 19 mai 2020 12:21
par mdanielm
Bonjour,
Je recherche une regex, une seule, qui en une passe seulement remplace
des séries de signes +, -, par un seul signe, en respectant la règle des signes:
+- ou -+ donne -
-- ou ++ donne +
Bref, une meilleure solution que la solution naïve suivante:
Re: Règle des signes
Posté : mar. 19 mai 2020 13:55
par TommyDDR
Comme dans cette règle, le "+" n'a aucune incidence, et seul un nombre pair de "-" donnent un "+", vous pouvez faire de la sorte :
Ce code fonctionne bien entendu que si vous fournissez une chaine avec seulement des + et des -, pas d'autre caractères, je vous laisse adapter le code si jamais vous avez besoin d'envoyer autre chose en plus.
Re: Règle des signes
Posté : mar. 19 mai 2020 14:08
par mikell
Re: Règle des signes
Posté : mar. 19 mai 2020 14:18
par mdanielm
J'ai édité le message d'origine car dans ma chaîne il y a d'autres caractères!
Re: Règle des signes
Posté : mar. 19 mai 2020 17:32
par TommyDDR
Bien joué Mikell ! J'oublie tout le temps que StringReplace range le nombre de remplacement dans @extended :p
Voilà la version prenant en compte les chiffres (grossomodo, on découpe la chaine en plus de +/- consécutif et on appelle la fonction précédente pour chaque bloc).
Re: Règle des signes
Posté : mar. 19 mai 2020 18:28
par mdanielm
Ta solution est bonne mais n'est pas plus simple que la mienne et surtout beaucoup plus lente.
Je rêvais d'une regex et d'une seule passe dans le texte.
Re: Règle des signes
Posté : mar. 19 mai 2020 18:32
par TommyDDR
Attendez la réponse de mikell, si c'est faisable en regex, il vous pondra la solution telle une poule pond naturellement son oeuf
Re: Règle des signes
Posté : mar. 19 mai 2020 21:06
par mdanielm
Re: Règle des signes
Posté : mar. 19 mai 2020 22:11
par jchd
En plus concis :
Code : Tout sélectionner
Local $in = "++-+3---5++(2*-+3)+-1-++-+---+-++-+-+4"
Signe($in)
ConsoleWrite($in & @LF)
Func Signe(ByRef $in)
$in = Execute("'" & StringRegExpReplace($in, "([+-]+)", "' & (Mod(StringLen(StringReplace('$1', '+', '')), 2) ? '-' : '+') & '") & "'")
EndFunc
Pour la ponte, j'imagine Mikell poursuivi par un coq en rut
Re: Règle des signes
Posté : mer. 20 mai 2020 01:17
par mdanielm
Je ne connaissais pas la méthode pour appliquer une fonction à $1.
On peut l'utiliser par exemple pour vérifier la présence d'une majuscule au début des phrases, et corriger éventuellement. A moins qu'il y ait plus simple?
Re: Règle des signes
Posté : mer. 20 mai 2020 02:31
par Tlem
Bonsoir.
Moi j'aurais proposé ceci :
Code : Tout sélectionner
Func Signe4(ByRef $in)
$in = StringReplace($in, "++", "+")
$in = StringReplace($in, "--", "+")
$in = StringReplace($in, "--", "+")
$in = StringReplace($in, "+-", "-")
$in = StringReplace($in, "-+", "-")
EndFunc
Qui est plus rapide et plus compréhensible qu'une expression régulière, malheureusement ce code ne fonctionne pas avec la chaine proposée par jchd.
Par contre ça fonctionne très bien avec la chaine proposée par mdanielm.
Re: Règle des signes
Posté : mer. 20 mai 2020 05:08
par jchd
C'est parce qu'un simple StringReplace, comme une expression régulière, ne marche "qu'en avant", c'est-à-dire qu'il ne revient pas sur ce qu'il vient de remplacer. La récursion dans PCRE ne concerne que des éléments de pattern, pas l'emplacement en cours dans le sujet.
Si on fait ça :
il se passe, en séquence (souligné = déjà été traité, gras = en cours de traitement) :
ZaaabaabaaabbW
ZaaabaaabbW
ZaaabaaabbW
ZaaaabbW
ZaaaabbW
ZaabW
ZaabW
Donc si l'entrée contient un nombre non borné de sous-chaînes à remplacer qui produiront d'autres sous-chaînes sujettes à remplacement, comme ci-dessus, il faudrait autant (donc un nombre non borné !) de StringReplace pour être certain d'effectuer tous les remplacements dans le cas général. Du coup c'est soit la récursion sur l'ensemble de la chaîne en cours de traitement, soit l'astuce à la Mikell qui fonctionne dans notre cas très particulier.
Que ce soit avec récursion ou dé-récursion (suite de remplacements explicites, en nombre forcément limité) il y a une limite au-delà de laquelle ça ne fonctionne plus (explosion de la pile ou dépassement du nombre maximum préu de remplacements) et ça a été la source d'innombrables bugs et "exploits" malveillants dans d'innombrables logiciels.
Seule une boucle non bornée (do .. until ou while 1 .. wend) apporte l'assurance de résultat correct dans tous les cas, ... sauf si l'entrée est un flux lui-même non borné, auquel cas on ouvre la voie à une paralysie partielle (DoS = denial of service) si on ne met pas un stéthoscope pour arrêter les frais.
Re: Règle des signes
Posté : mer. 20 mai 2020 09:30
par Tlem
Merci JC.
En fait, avant d'écrire mon message, j'en était arrivé à ceci :
Code : Tout sélectionner
Func Signe4(ByRef $in)
Local $StrLen
While 1
$in = StringReplace($in, "++", "+")
$in = StringReplace($in, "--", "+")
$in = StringReplace($in, "--", "+")
$in = StringReplace($in, "+-", "-")
$in = StringReplace($in, "-+", "-")
If $StrLen = StringLen($in) Then ExitLoop
$StrLen = StringLen($in)
WEnd
EndFunc ;==>Signe4
Mais non seulement ce n'est pas un code plus court, mais en plus ce code met deux fois plus de temps par rapport à la fonction Signe1.
Après, avec la fonction Signe1 (qui est la plus rapide de toutes) et pour grappiller quelques milliseconde
, on peux aussi faire ça :
Code : Tout sélectionner
Func Signe5(ByRef $in)
Local $StrLen
While 1
$in = StringRegExpReplace($in, "\+-|-\+", "-")
$in = StringRegExpReplace($in, "\+\+|--", "+")
If $StrLen = StringLen($in) Then ExitLoop
$StrLen = StringLen($in)
WEnd
Return $in
EndFunc ;==>Signe5
Sur une boucle de 100000 traitements, on arrive à gagner 2 à 4 secondes, mais bon ...
Re: Règle des signes
Posté : mer. 20 mai 2020 10:40
par jchd
Je me suis conformé au cahier des charges :
Je recherche une regex, une seule, qui en une passe seulement remplace des séries de signes +, -, par un seul signe, en respectant la règle des signes
Re: Règle des signes
Posté : mer. 20 mai 2020 10:53
par jchd
Je ne connaissais pas la méthode pour appliquer une fonction à $1.
On peut l'utiliser par exemple pour vérifier la présence d'une majuscule au début des phrases, et corriger éventuellement. A moins qu'il y ait plus simple?
C'est un brin délicat à claviotter au début, à cause de l'imbrication des quotes. Mais on peut l'employer pour faire de l'interception (enfin presque) par du code natif, un peu comme Perl le permet. Il y a des limitations car c'est seulement à la fin de la partie regexp, quand tout est joué, qu'on peut appliquer le code voulu au(x) groupe(s), capturés ou pas, dans la partie "replace". Alors que Perl permet d'appliquer du code natif à tout moment pendant la regexp.
J'aimerais beaucoup avoir le temps de déployer une UDF complète pour wrapper les APIs de PCRE2, mais c'est un bien trop gros morceau pour le peu de temps libre dont je dispose.
Re: Règle des signes
Posté : mer. 20 mai 2020 21:35
par mikell
jchd a écrit : ↑mar. 19 mai 2020 22:11Pour la ponte, j'imagine Mikell poursuivi par un coq en rut
C'est absolument hors de question
- ohh.jpg (5.72 Kio) Vu 10598 fois
Sinon, juste une petite UDF - même sommaire - pour faciliter l'emploi du SRER dans un Execute, ça serait super
J'ai toujours eu du mal avec ces fichues quotes
Re: Règle des signes
Posté : mer. 20 mai 2020 22:21
par jchd
Quelque chose comme ça ?
Code : Tout sélectionner
Local $s = "++-+3---5++(2*-+3)+-1-++-+---+-++-+-+4"
Local $in = $s
Signe($in)
ConsoleWrite($in & @LF)
Local $sToDo = "(Mod(StringLen(StringReplace('$1', '+', '')), 2) ? '-' : '+')"
_RegExpReplacePostProc($s, "([+-]+)", $sToDo)
MsgBox(0, "", $s)
Func Signe(ByRef $s)
$s = Execute("'" & StringRegExpReplace($s, "([+-]+)", "' & (Mod(StringLen(StringReplace('$1', '+', '')), 2) ? '-' : '+') & '") & "'")
EndFunc
Func _RegExpReplacePostProc(ByRef $sSubject, $sPattern, $sCode)
$sSubject = Execute("'" & StringRegExpReplace($sSubject, $sPattern, "' & " & $sCode & " & '") & "'")
EndFunc
Bon, c'est brut de cul de poule (mpff !) mais ça marche.
On peut aussi prévoir de passer une array de codes différents (toujours en string) à appliquer à $1, $2, $3, ... mais le problème est alors de ficeler ensemble toutes les parties et plus il y en a plus ça devient tortueux.
Faut oublier, c'est ingérable !
Re: Règle des signes
Posté : jeu. 21 mai 2020 08:58
par mikell
Oui quelque chose comme ça...
Avec une backreference c'est déjà bien
Je vais essayer de trafiquer ta fonction pour qu'elle affiche l'instruction finale avant son exécution
Re: Règle des signes
Posté : jeu. 21 mai 2020 13:14
par jchd
J'ai regardé vite fait : 90% des utilisations que j'en ai eu (pour moi ou pour répondre à des demandes) il n'y avait qu'un groupe ($1) et le reste, quand on a besoin de $1, $2, $3 ... on peut tout grouper dans une seule expression/fonction.
Donc je dirais qu'en cas de besoin plus sophistiqué, on cpature tout ce qu'il faut et on traite les morceaux avec le code ad hoc.
Re: Règle des signes
Posté : jeu. 21 mai 2020 18:47
par mikell
Très pratique en tout cas pour mon petit blocage avec les quotes...
Du coup je me suis fait la petite gui qui va bien
c'est pas infaillible mais ça va rudement me simplifier la vie
Purée comment j'y ai pas pensé avant ?
$gui = GuiCreate("constructeur Execute SRER", 950, 110, -1, 100)GUISetFont(12, 400, 0, "Times New Roman", $gui, 5)GuiCtrlCreateLabel("pattern", 20, 8, 100, 20)GuiCtrlCreateLabel("remplacement (utiliser simples quotes)", 390, 8, 300, 20)$input1 = GuiCtrlCreateInput("", 10, 30, 360, 25)$input2 = GuiCtrlCreateInput("", 380, 30, 500, 25)$btn = GuiCtrlCreateButton("go", 890, 30, 50, 25)$input3 = GuiCtrlCreateInput("", 10, 70, 930, 25)GuiSetState()While 1 $msg = GuiGetMsg() If $msg = -3 Then Exit If $msg = $btn Then $tomatch = GuiCtrlRead($input1) $todo = GuiCtrlRead($input2) $instr = 'Execute("''" & StringRegExpReplace($s, "' & $tomatch & '", "'' & ' & $todo & ' & ''") & "''")' GuiCtrlSetData($input3, $instr) EndIfWend