Aller au contenu

(gile)

Moderateurs
  • Compteur de contenus

    12 247
  • Inscription

  • Dernière visite

  • Jours gagnés

    209

Tout ce qui a été posté par (gile)

  1. Quelques exemples. RegExpTest est un peu l'équivalent de wcmatch mais permet une recherche plus fine. Par exemple, pour savoir si une chaîne représente un nombre entier positif, autrement dit si elle est constituée uniquement de un ou plusieurs chiffres. (RegExpTest "42" "^\\d+$" T) "^\\d+$" : ^ début de la chaîne \\d+ un ou plusieurs chiffres $ fin de la chaîne Ou si la chaîne représente un nombre (entier ou réel positif ou négatif) (RegExpTest "-2.5" "^-?\\d+(\\.\\d+)?$" T) "^-?\\d+(\\.\\d+)?$" ^ début de la chaîne -? zéro ou une fois le caractère '-' \\d+ un ou plusieurs chiffres (\\.\\d+)? zéro ou une fois un point suivi de un ou plusieurs chiffres $ fin de la chaîne RegExpExecute permet de récupérer le résultat complet d'une recherche et de constituer des groupements de correspondances. (RegExpExecute "B12" "([A-Z]+)(\\d+)" T nil) "([A-Z]+)(\\d+)" ([A-Z]+) groupe de une ou plusieurs lettres (\\d+) groupe de un ou plusieurs chiffres Cette expression retourne : (("B12" 0 ("B" "12"))) et on peut facilement récupérer d'un côté la valeur littérale (colonne d'une cellule) et de l'autre la valeur numérique (rangée d'une cellule). RegExpReplace permet de remplacer plusieurs correspondances (avec global /= nil) en utilisant des groupes dans la chaîne de remplacement. Soit une chaîne représentant le texte d'un texte multiligne contenant des formatages : (setq str "{\\C1;foo} {\\L\\C30;bar}") Supprimer uniquement les formatages de couleur : (setq str (RegExpReplace str "\\\\C\\d+;" "" T T)) "\\\\C\\d+;" \\\\C le caractère '\\C' \\d+ un ou plusieurs chiffres ; le caractère ';' Cette expression retourne : "{foo} {\\Lbar}" Supprimer les accolades uniquement si elles ne contiennent pas de caractère '\\' : (setq str (RegExpReplace str "{([^\{\\\\]+)}" "$1" nil T)) Le motif crée des groupes avec la partie de la chaîne qui se trouve entre accolades et ne contient pas de caractère '\\'. "{([^\{\\\\]+)}" { caractère '{' ( début d'un groupement [^ ne contient pas les caractères \{ accolade ouvrante '{' \\\\ double anti-slash '\\' ] fin des caractères exclus + une fois ou plus ) fin de groupement } caractère '}' Cette expression retourne : "foo {\\Lbar}" Petite explication. Avec RegexpExecute : (RegExpExecute "{foo} {\\Lbar}" "{([^\{\\\\]+)}" nil T)retourne : (("{foo}" 0 ("foo"))) On voit une occurrence trouvée "{foo}" à l'index 0 et une liste des groupements ("foo"). Le symbole "$1" utilisé comme chaîne de remplacement correspond au premier groupe dans cette liste, soit "foo" ; l'occurrence "{foo}" est remplacée par "foo". De cette dernière, on peut tirer une petite routine utile : (defun StripMtextColor (mtext / str) (setq str (getpropertyvalue mtext "Contents")) (setq str (RegexpReplace str "\\\\C\\d+;" "" T T)) (setq str (RegexpReplace str "{([^\\\\\{]+)}" "$1" T T)) (setpropertyvalue mtext "Contents" str) )
  2. Salut Il en avait été question (sans succès) dans ce sujet, et ces dernier temps j'ai eu à m'en servir ici en LISP et là en C#. Les expressions régulières permettent de rechercher les correspondances avec un motif dans une chaîne de caractère, un peu comme wcmatch mais avec la possibilité de récupérer ou de remplacer ces correspondances. Les caractères utilisés pour la définition du motif (ou modèle, pattern en anglais) sont beaucoup plus nombreux que les caractères génériques utilisés avec wcmatch, ce qui fait des expression régulières un mécanisme beaucoup plus puissant, mais aussi plus obscur quant à la création des motifs (voir ce tableau récapitulatif). L'objet RegExp de VBScript, accessible en LISP avec ActiveX, expose trois propriétés et trois méthodes que j'ai pensé utile d'implémenter en LISP à travers quatre fonctions. ;;--------------------------------------------------------------------------------------------------------;; ;; Expressions Régulières ;; ;; ;; ;; Référence ;; ;; http://tahe.developpez.com/tutoriels-cours/vbscript/?page=page_4#LIV-B ;; ;; https://msdn.microsoft.com/en-us/library/1400241x(VS.85).aspx ;; ;;--------------------------------------------------------------------------------------------------------;; (vl-load-com) ;; RegExpSet ;; Retourne l'instance de VBScript.RegExp courante après avoir défini ses propriétés. ;; ;; Arguments ;; pattern : Motif à rechercher. ;; ignoreCase : Si non nil, la recherche est faite sans tenir compte de la casse. ;; global : Si non nil, recherche toutes les occurrences du modèle ; ;; si nil, recherche uniquement la première occurrence. (defun RegExpSet (pattern ignoreCase global / regex) (setq regex (cond ((vl-bb-ref '*regexp*)) ((vl-bb-set '*regexp* (vlax-create-object "VBScript.RegExp"))) ) ) (vlax-put regex 'Pattern pattern) (if ignoreCase (vlax-put regex 'IgnoreCase acTrue) (vlax-put regex 'IgnoreCase acFalse) ) (if global (vlax-put regex 'Global acTrue) (vlax-put regex 'Global acFalse) ) regex ) ;; RegexpTest ;; Retourne T si une correspondance avec le motif a été trouvée dans la chaîne ; sinon, nil. ;; ;; Arguments ;; string : Chaîne dans la quelle on recherche le motif. ;; pattern : Motif à rechercher. ;; ignoreCase : Si non nil, la recherche est faite sans tenir compte de la casse. ;; ;; Exemples : ;; (RegexpTest "foo bar" "Ba" nil) ; => nil ;; (RegexpTest "foo bar" "Ba" T) ; => T ;; (RegExpTest "42C" "[0-9]+" nil) ; => T (defun RegexpTest (string pattern ignoreCase) (= (vlax-invoke (RegExpSet pattern ignoreCase nil) 'Test string) -1) ) ;; RegExpExecute ;; Retourne la liste des correspondances avec le motif trouvées dans la chaine. ;; Chaque correspondance est renvoyée sous la forme d'une sous-liste contenant : ;; - la valeur de la correspondance, ;; - l'index du premier caractère (base 0) ;; - une liste des sous groupes. ;; ;; Arguments ;; string : Chaîne dans la quelle on recherche le motif. ;; pattern : Motif à rechercher. ;; ignoreCase : Si non nil, la recherche est faite sans tenir compte de la casse. ;; global : Si non nil, recherche toutes les occurrences du modèle ; ;; si nil, recherche uniquement la première occurrence. ;; ;; Exemples ;; (RegExpExecute "foo bar baz" "ba" nil nil) ; => (("ba" 4 nil)) ;; (RegexpExecute "12B 4bis" "([0-9]+)([A-Z]+)" T T) ; => (("12B" 0 ("12" "B")) ("4bis" 4 ("4" "bis"))) ;; (RegexpExecute "-12 25.4" "(-?\\d+(?:\\.\\d+)?)" nil T) ; => (("-12" 0 ("-12")) ("25.4" 4 ("25.4"))) (defun RegExpExecute (string pattern ignoreCase global / sublst lst) (vlax-for match (vlax-invoke (RegExpSet pattern ignoreCase global) 'Execute string) (setq sublst nil) (vl-catch-all-apply '(lambda () (vlax-for submatch (vlax-get match 'SubMatches) (if submatch (setq sublst (cons submatch sublst)) ) ) ) ) (setq lst (cons (list (vlax-get match 'Value) (vlax-get match 'FirstIndex) (reverse sublst) ) lst ) ) ) (reverse lst) ) ;; RegExpReplace ;; Retourne la chaîne après remplacement des correspondances avec le motif. ;; ;; Arguments ;; string : Chaîne dans la quelle on recherche le motif. ;; pattern : Motif à rechercher. ;; newStr : Chaîne de remplacement. ;; ignoreCase : Si non nil, la recherche est faite sans tenir compte de la casse. ;; global : Si non nil, recherche toutes les occurrences du modèle ; ;; si nil, recherche uniquement la première occurrence. ;; ;; Exemples : ;; (RegexpReplace "foo bar baz" "a" "oo" nil T) ; => "foo boor booz" ;; (RegexpReplace "foo bar baz" "(\\w)\\w(\\w)" "$1U$2" nil T) ; => "fUo bUr bUz" ;; (RegexpReplace "$ 3.25" "\\$ (\\d+(\\.\\d+)?)" "$1 €" nil T) ; => "3.25 €" (defun RegExpReplace (string pattern newStr ignoreCase global) (vlax-invoke (RegExpSet pattern ignoreCase global) 'Replace string newStr) )
  3. (gile)

    (résolu) StripMtext

    Salut, Tu peux essayer ce petit truc vite fait : (defun c:StripMTextColorInBlocks (/ regex) (vl-load-com) (or *acad* (setq *acad* (vlax-get-acad-object))) (or *acdoc* (setq *acdoc* (vla-get-ActiveDocument *acad*))) (or *blocks* (setq *blocks* (vla-get-Blocks *acdoc*))) (setq regex (vlax-create-object "VBScript.RegExp")) (vlax-put regex 'Pattern "\\\\[Cc][0-9]?[.]?[0-9]+;") (vlax-for blk *blocks* (if (and (= (vla-get-IsLayout blk) :vlax-false) (= (vla-get-IsXRef blk) :vlax-false) ) (vlax-for obj blk (if (= (vla-get-ObjectName obj) "AcDbMText") (vla-put-textstring obj (vlax-invoke regex 'Replace (vla-get-textstring obj) "") ) ) ) ) ) (vlax-release-object regex) (vla-Regen *acdoc* acAllViewports) (princ) )
  4. (gile)

    Bloc et lisp

    Salut, Tu ne peux pas appeler _INSERT de manière transparente tant que la commande _PLINE est en cours. Par contre, tu peux stocker les points spécifiés dans une liste et, une fois sorti de la commande _PLINE insérer un bloc à chaque point. (setq pts nil) (command "_pline") (while (setq pt (getpoint "\nPoint suivant: ")) (command "_non" pt) (setq pts (cons pt pts)) ) (command) (foreach p pts (command "_insert" "NomDuBloc" "_non" p 1. 1. 0.) )
  5. Salut, La fonction entget permet de récupérer les données DXF d'une entité. Ces données ne permettent pas d'obtenir directement la longueur. On pourrait la calculer à partir des coordonnées des sommets (et des bulges en cas d'arcs), mais il y a plus simple. Depuis AutoCAD 4 nouvelles fonctions LISP sont apparues pour palier tant soit peu l'absence de l'API COM (utilisé par les fonction vla*) dans AutoCAD MAC. L'idée, en phase de développement, est de dessiner une polyligne et de faire : (dumpallproperties (entlast))pour voir dans la fenêtre de texte (F2) les propriétés disponibles. Tu devrais trouver une propriété Length que tu peux utiliser ensuite avec getpropertyvalue : (setq longueur (getpropertyvalue (entlast) "Length"))
  6. Je ne te le fais pas dire, et VB .NET conserve ce genre d'approximations.
  7. Mes réponses n'étaient pas claires !!??
  8. À ma connaissance, il n'y a jamais eu d'enregistreur de MACRO VBA dans AutoCAD. Il y a un environnement de développement intégré (éditeur de code) VBAIDE (à condition d'avoir installé le module VBA). Cet éditeur est l'équivalent pour VBA de l'éditeur Visual LISP (VLIDE) pour AutoLISP, mais dans l'un comme dans l'autre il faut "coder" à la main.
  9. Non, l'enregistreur d'action génère des fichiers .actm cryptés. Je rappelle que le VBA devait être abandonné par Microsoft (et donc par Autodesk) avec l'arrivée du 64 bits, qu'il a été finalement mis à jour après de longues hésitations grâce (ou à cause) des réactions des utilisateurs. Depuis, le module VBA n'est plus installé dans AutoCAD par défaut (il faut une installation spécifique). Aujourd'hui Microsoft (comme Autodesk) préconisent d'utiliser plutôt .NET même si, de l'avis de tous les utilisateurs, "on ne joue plus dans la même cour".
  10. :blink: ??? Ce que tu dis m'étonne beaucoup (AutoCAD n'est pas Excel). Je n'ai jamais utilisé l'enregistreur de macro mais il me semble bien qu'il est arrivé dans AutoCAD au moment où le VBA devait être abandonné.
  11. Salut, Je ne connais pas bien le VBA (je suis allergique à la syntaxe VB) mais je pense pouvoir dire que le code que que tu as posté est incomplet.
  12. Il suffit de supprimer la ligne (ou de la mettre en commentaire avec deux points-virgules au début) : (vla-AddLine space (vlax-3d-point cen) (vlax-3d-point pt))
  13. Voilà une version plus aboutie. (defun c:cotcirc (/ *error* space cen pt ang ht just rot text) (vl-load-com) (or *acdoc* (setq *acdoc* (vla-get-ActiveDocument (vlax-get-acad-object))) ) (defun *error* (msg) (and msg (/= msg "Fonction annulée") (prompt (strcat "\nErreur : " msg)) ) (vla-EndUndoMark *acdoc*) (princ) ) (if (setq cen (getpoint "\nCentre de la cotation: ")) (progn (vla-StartUndoMark *acdoc*) (setq space (vla-get-Block (vla-get-ActiveLayout *acdoc*))) (while (setq pt (getpoint cen "\nExtémité de l'angle: ")) (setq pt (trans pt 1 0) cen (trans cen 1 0) ang (angle cen pt) ht (getvar 'textsize)) (cond ((equal ang (* pi 0.5) 1e-9) (setq just acAlignmentBottomCenter rot 0.0 ) ) ((equal ang (* pi 1.5) 1e-9) (setq just acAlignmentTopCenter rot 0.0 ) ) ((< (* pi 0.5) ang (* pi 1.5)) (setq just acAlignmentMiddleRight rot (+ pi ang) ) ) (T (setq just acAlignmentMiddleLeft rot ang ) ) ) (vla-AddLine space (vlax-3d-point cen) (vlax-3d-point pt)) (setq text (vla-AddText space (strcat (angtos (- (* 0.5 pi) ang)) "°") (vlax-3d-point pt) ht)) (vla-put-Rotation text rot) (vla-put-Alignment text just) (vla-put-TextAlignmentPoint text (vlax-3d-point pt)) ) ) ) (*error* nil) )
  14. C'est nicolas2 qui a déterré ce sujet et évoquer un LISP pour son problème. Il aurait peut-être dû commencer un nouveau sujet, mais bon, c'est fait.
  15. 1- Il suffit de définir deux styles de cotation. 2- 3- C'est plus compliqué en utilisant les cotations. Je te propose une solution avec juste des lignes et des textes. (defun c:test (/ cen org dist ang0 ang pt) (if (and (setq cen (getpoint "\nCentre de la cotation: ")) (setq org (getpoint cen "\nExtrémité de l'axe de base: ")) ) (progn (setq dist (distance cen org) ang0 (angle cen org)) (command "_.line" "_non" cen "_non" org "") (command "_.text" "_justify" "_BC" "_non" org "" 0.0 "0°") (setq pt (polar cen (+ pi ang0) dist)) (command "_.line" "_non" cen "_non" pt "") (command "_.text" "_justify" "_TC" "_non" pt "" 0.0 "180°") (setq pt (polar cen (- ang0 (* 0.5 pi)) dist)) (command "_.line" "_non" cen "_non" pt "") (command "_.text" "_justify" "_ML" "_non" pt "" 0.0 "90°") (setq pt (polar cen (- ang0 (* 1.5 pi)) dist)) (command "_.line" "_non" cen "_non" pt "") (command "_.text" "_justify" "_MR" "_non" pt "" 0.0 "270°") (while (setq pt (getpoint cen "\nExtrémité de l'angle suivant: ")) (setq ang (angle cen pt)) (command "_.line" "_non" cen "_non" pt "") (command "_.text" "_justify" "_ML" "_non" pt "" (angtos ang (getvar 'aunits) 16) (strcat (angtos (- ang0 ang)) "°") ) ) ) ) (princ) )
  16. OK, je te propose un petit LISP, il faut que dans le style de cote, la "Position du texte", case "Horizontal" soit sur : "Au dessus de la ligne d'attache 1". (defun c:test (/ cen org pt) (if (and (setq cen (getpoint "\nSommet de l'angle: ")) (setq org (getpoint cen "\nExtrémité de l'axe de base: ")) ) (progn (command "_.DIMANGULAR" "" "non" cen "non" org "non" org "non" org) (while (setq pt (getpoint cen "\nExtrémité de l'angle suivant: ")) (command "_.DIMANGULAR" "" "non" cen "non" pt "non" org "non" pt) ) ) ) (princ) )
  17. Salut, Je ne suis pas sûr de comprendre la demande. Il me semble que s'il s'agit de reproduire l'image en lien, un bloc devrait faire l'affaire.
  18. N'exagérons rien, tout ça s'apprend, en 2000 je n'avais pas d'ordi.
  19. Salut, Pourtant la problématique est simple : trier des chaînes de caractères figurant des numéros de rue ; soit des chaînes commençant par un ou plusieurs chiffres, éventuellement suivis de lettres. Par exemple "12", "5", "5B", "5A". On connait bien les problèmes posés par le tri alphabétique de chaînes figurant des nombres : "12" se retrouve avant "5". S'ajoute ici la question du suffixe. Il faut donc trouver une moyen de trier d'abord suivant la valeur numérique (nombre entier) la partie numérique au début de la chaîne, puis, en cas d'égalité de cette valeur, de trier de manière alphabétique le reste de la chaîne. Pour implémenter l'algorithme général ci-dessus, il faut trouver un moyen de séparer la partie numérique du début de la chaine du reste. On pourrait écrire une fonction, mais, malgré l'adage*, j'ai pensé qu'utiliser les expressions régulières était un moyen tout à fait adapté. Les expressions régulières constituent une technique aussi puissante qu'obscure pour traiter des chaînes de caractères. La classe .NET Regex utilise cette technique et permet à l'aide de modèles (patterns) de rechercher, modifier, récupérer des occurrences de chaînes dans une chaîne de caractère. Le "côté obscur" des expressions régulières, c'est la définition du modèle à l'aide des nombreux caractères spéciaux. Le modèle dans notre exemple est : @"(?i)^([0-9]+)([A-Z]*)$" (?i) indique qu'on veut ignorer la casse (on aurait aussi pu utiliser l'option RegexOptions.IgnoreCase ^ indique que le caractère suivant doit se trouver en début de chaîne ([0-9]+) les parenthèses indiquent un groupe constitué ici d'un ou plusieurs caractère numériques ([A-Z]*) indique un autre groupe constitué de zéro ou plusieurs caractères alphabétiques (donc éventuellement une chaîne vide) $ indique que le caractère précédent doit se trouver en fin de chaîne Mais une fois le modèle élaboré, la méthode Regex.Match() permet à la fois de tester la validité du format de la chaîne (propriété Success) et, si le format est valide, de récupérer la partie numérique d'un côté et le suffixe de l'autre (propriété Groups) On peut ensuite comparer ces deux bouts de chaîne - soit dans l'implémentation de la méthode Compare() d'une classe implémentant IComparer qu'on poura utiliser comme argument avec List.Sort() ou avec IEnumerable.OrderBy() (CF réponse #4). - soit dans les méthodes CompareTo() d'une classe définissant un objet directement triable en implémentant IComaprable et IComparable (il est préférable d'implémenter les deux la méthode CompareTo() de IComparable appelant celle de IComaprable) (CF réponse #7). * à propos des expressions régulières, on entend parfois : "Certaines personnes, confrontées à un problème, pensent : "Je vais utiliser les expressions régulières". Maintenant, elles ont deux problèmes."
  20. Une version commentée de la classe StreetNumberComparer. class StreetNumberComparer : IComparer<string> { public int Compare(string x, string y) { //var regex = new Regex(@"(?i)^([0-9]+)([A-Z]*)$"); var regex = new Regex(@" (?i) # ignorer la casse ^ # début de la chaîne ([0-9]+) # un ou plusieurs chiffres (groupe 1) ([A-Z]*) # zéro ou plusieurs lettres (groupe 2) $ # fin de la chaîne ", RegexOptions.IgnorePatternWhitespace); // option pour modèle avec des commentaires // évalue si la chaîne x correspond au modèle et constitue les groupes var matchX = regex.Match(x); if (!matchX.Success) throw new FormatException($"Numéro de rue non valide : {x}"); // évalue si la chaîne y correspond au modèle et constitue les groupes var matchY = regex.Match(y); if (!matchY.Success) throw new FormatException($"Numéro de rue non valide : {y}"); // compare les valeurs numériques des deux numéros (groupe 1) var compareNumber = int.Parse(matchX.Groups[1].Value).CompareTo(int.Parse(matchY.Groups[1].Value)); if (compareNumber == 0) // si les numéros sont égaux // compare les deux suffixes des numéros (groupe 2) return compareNumber = matchX.Groups[2].Value.ToUpper().CompareTo(matchY.Groups[2].Value.ToUpper()); else return compareNumber; } }
  21. Même pas dans mes pires cauchemars !... Par contre, je travaillerais volontiers avec Olivier, depuis Marseille. ;)
  22. Ça m'amusait et il m'est arrivé d'avoir à utiliser des adresses postales (dans un programme de distribution de fibre optique). Donc une petite classe qui définit un type Adresse (ici juste le numéro et le nom de la rue) et qui utilise un type NumeroDeRue (numéro avec éventuellement un suffixe numérique). Ces deux type sont "triables" (implémentation de IComparable et IComparable) Adresse using System; using System.Text.RegularExpressions; namespace Gile.AdressePostale { /// <summary> /// Décit une adresse postale interne à une ville (numéro et nom de rue). /// </summary> public class Adresse : IComparable, IComparable<Adresse> { string street, text; NumeroDeRue number; /// <summary> /// Obtient le numéro. /// </summary> public NumeroDeRue Numero => number; /// <summary> /// Obtient le nom de la rue. /// </summary> public string Rue => street; /// <summary> /// Crée une nouvelle instance de Adresse. /// </summary> /// <param name="number">Numéro (avec éventuellement un suffixe alphabétique).</param> /// <param name="street">Nom de la rue.</param> public Adresse(string number, string street) { try { this.number = new NumeroDeRue(number); } catch { throw; } this.street = street ?? throw new ArgumentNullException("street"); text = number + " " + street; } /// <summary> /// Crée une nouvelle instance de Adresse. /// </summary> /// <param name="address">Adresse (numéro rue).</param> public Adresse(string address) { if (address == null) throw new ArgumentNullException("adress"); var regex = new Regex(@"(?i)^(?<number>\d+[A-Z]*)\s+(?<street>.+)$"); var match = regex.Match(address); if (!match.Success) throw new FormatException($"Format d'adresse non valide {address}"); text = address; number = new NumeroDeRue(match.Groups["number"].Value); street = match.Groups["street"].Value; } /// <summary> /// Compare l'instance actuelle avec un autre objet du même type et retourne un entier qui indique /// si l'instance actuelle précède ou suit un autre objet ou se trouve à la même position dans l'ordre de tri. /// </summary> /// <param name="obj">Objet à comparer à cette instance.</param> /// <returns>Valeur qui indique l'ordre relatif des objets comparés.</returns> public int CompareTo(object obj) { if (obj != null && !(obj is Adresse)) throw new ArgumentException("L'objet n'est pas de type Adresse"); return CompareTo((Adresse)obj); } /// <summary> /// Compare l'instance actuelle avec un autre objet du même type et retourne un entier qui indique /// si l'instance actuelle précède ou suit un autre objet ou se trouve à la même position dans l'ordre de tri. /// </summary> /// <param name="other">Instance de Adresse à comparer à cette instance.</param> /// <returns>Valeur qui indique l'ordre relatif des objets comparés.</returns> public int CompareTo(Adresse other) { if (other == null) return 1; int compareStreet = string.Compare(street, other.street, true); return compareStreet == 0 ? number.CompareTo(other.number) : compareStreet; } /// <summary> /// Détermine si l'objet spécifié est identique à l'objet actuel. /// </summary> /// <param name="obj">Objet à comparer à l'objet actuel.</param> /// <returns>true si l'objet spécifié est égal à l'objet actuel ; sinon, false.</returns> public override bool Equals(object obj) => obj is Adresse && text.ToUpper().Equals(((Adresse)obj).text.ToUpper()); /// <summary> /// Fait office de fonction de hachage par défaut. /// </summary> /// <returns>Code de hachage pour l'objet actuel.</returns> public override int GetHashCode() => text.ToUpper().GetHashCode(); /// <summary> /// Retourne une chaîne qui représente l'objet actuel. /// </summary> /// <returns>Chaîne qui représente l'objet actuel.</returns> public override string ToString() => text; } } NumeroDeRue using System; using System.Text.RegularExpressions; namespace Gile.AdressePostale { /// <summary> /// Décrit un numéro de rue contenant éventuellement un suffixe alphabétique. /// </summary> public class NumeroDeRue : IComparable, IComparable<NumeroDeRue> { int number; string suffix, text; /// <summary> /// Obtient le numéro. /// </summary> public int Numero => number; /// <summary> /// Obtient le suffixe /// </summary> public string Suffixe => suffix; /// <summary> /// Crée une nouvelle instance de NumeroDeRue. /// </summary> /// <param name="streetNumber">Numéro complet.</param> public NumeroDeRue(string streetNumber) { text = streetNumber ?? throw new ArgumentNullException("streetNumber"); var regex = new Regex(@"(?i)^(\d+)([A-Z]*)$"); var match = regex.Match(text); if (!match.Success) throw new FormatException($"Format de numéro de rue non valide : {streetNumber}"); number = int.Parse(match.Groups[1].Value); suffix = match.Groups[2].Value; } /// <summary> /// Compare l'instance actuelle avec un autre objet du même type et retourne un entier qui indique /// si l'instance actuelle précède ou suit un autre objet ou se trouve à la même position dans l'ordre de tri. /// </summary> /// <param name="obj">Objet à comparer à cette instance.</param> /// <returns>Valeur qui indique l'ordre relatif des objets comparés.</returns> public int CompareTo(object obj) { if (obj != null && !(obj is NumeroDeRue)) throw new ArgumentException("L'objet n'est pas de type NumeroDeRue"); return CompareTo((NumeroDeRue)obj); } /// <summary> /// Compare l'instance actuelle avec un autre objet du même type et retourne un entier qui indique /// si l'instance actuelle précède ou suit un autre objet ou se trouve à la même position dans l'ordre de tri. /// </summary> /// <param name="other">Instance de NumeroDeRue à comparer à cette instance.</param> /// <returns>Valeur qui indique l'ordre relatif des objets comparés.</returns> public int CompareTo(NumeroDeRue other) { if (other == null) return 1; var compareNumber = number.CompareTo(other.number); return compareNumber == 0 ? string.Compare(suffix, other.suffix, true) : compareNumber; } /// <summary> /// Détermine si l'objet spécifié est identique à l'objet actuel. /// </summary> /// <param name="obj">Objet à comparer à l'objet actuel.</param> /// <returns>true si l'objet spécifié est égal à l'objet actuel ; sinon, false.</returns> public override bool Equals(object obj) => obj is NumeroDeRue && text.ToUpper().Equals(((NumeroDeRue)obj).text.ToUpper()); /// <summary> /// Fait office de fonction de hachage par défaut. /// </summary> /// <returns>Code de hachage pour l'objet actuel.</returns> public override int GetHashCode() => text.ToUpper().GetHashCode(); /// <summary> /// Retourne une chaîne qui représente l'objet actuel. /// </summary> /// <returns>Chaîne qui représente l'objet actuel.</returns> public override string ToString() => text; } } Un exemple d'utilisation : using Gile.AdressePostale; using System; using System.Linq; namespace ConsoleApp { class Program { static void Main(string[] args) { string[] adresses = new[] { "8 avenue John McCarthy", "5b rue du Code", "14 avenue John McCarthy", "5a rue du Code", "5 rue du Code", }; foreach (var item in adresses.OrderBy(a => new Adresse(a))) { Console.WriteLine(item); } Console.WriteLine(); foreach (var item in adresses.Select(a => new Adresse(a)).GroupBy(a => a.Rue)) { Console.WriteLine($"{item.Key} : {item.Count()} adresses"); } } } } Résultat : 8 avenue John McCarthy 14 avenue John McCarthy 5 rue du Code 5a rue du Code 5b rue du Code avenue John McCarthy : 2 adresses rue du Code : 3 adresses
  23. Salut, D'après ce que je vois dans le code source ici ou là, il n'y a aucune référence aux bibliothèque d'AutoCAD, donc il n'y a aucune raison qu'il y ait une version spécifique pour AutoCAD 2018.
  24. J'ai corrigé une erreur dans le code. Suivant ce que tu as à faire, il peut être intéressant de définir une classe Adresse qui implémente IComparable avec des propriétés Rue (de type string) et Numero (d'un type StreetNumber qui implémente aussi IComparable comme ci-dessus).
  25. Salut, pour le tri des numéros de rue, tu peux utiliser un comparateur du style : EDIT : code corrigé class StreetNumberComparer : IComparer<string> { public int Compare(string x, string y) { var regex = new Regex(@"(?i)^([0-9]+)([A-Z]*)$"); var matchX = regex.Match(x); if (!matchX.Success) throw new FormatException($"Numéro de rue non valide : {x}"); var matchY = regex.Match(y); if (!matchY.Success) throw new FormatException($"Numéro de rue non valide : {y}"); var compareNumber = int.Parse(matchX.Groups[1].Value).CompareTo(int.Parse(matchY.Groups[1].Value)); if (compareNumber == 0) return compareNumber = matchX.Groups[2].Value.ToUpper().CompareTo(matchY.Groups[2].Value.ToUpper()); else return compareNumber; } } Exemple : var numbers = new List<string> { "9", "10B", "243", "10a", "10" }; numbers.Sort(new StreetNumberComparer()); numbers : "9", "10", "10a", "10B", "243"
×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer. Politique de confidentialité