IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VB.NET Discussion :

Optimisation remplacement de caractères (ãäåæçèéêëìíîï)


Sujet :

VB.NET

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert Avatar de _Ez3kiel
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2013
    Messages
    836
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Janvier 2013
    Messages : 836
    Par défaut Optimisation remplacement de caractères (ãäåæçèéêëìíîï)
    Bonjour !

    J'essaye de faire une fonction de remplacement de caractère qui sera utilisé très souvent ainsi j'essaye de l'optimiser autant que possible, et c'est pourquoi je me tourne vers vous. J'ai 2 algos et je n'arrive pas à déterminer lequel pourrait être le plus performant ... Car c'est variable selon la chaîne à traiter ...

    Quel est selon votre logique, le plus performant des 2 ? Auriez-vous une autre idée pour booster encore ? J'avais songé aux regex, mais je suis pas sûr que ce soit plus rapide ...

    Merci de votre avis !


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
        Public Shared Function formaterCaractereSpeciaux(ByVal chaineR As String) As String
            Dim i As Integer
     
            Const CS = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåæçèéêëìíîïù"
            Const CN = "AAAAAAACEEEEIIIIDNOOOOOUUUUUaaaaaaaceeeeiiiiu"
            Const CR = "²&~#{([|`_\^@)]°}=+^¨$£¤%µ*,?;:/!§<>"
     
     
            For i = 0 To CN.Length - 1
                chaineR = chaineR.Replace(CS(i), CN(i))
            Next
     
            For i = 0 To CR.Length - 1
                chaineR = chaineR.Replace(CR(i), " ")
            Next
     
     
            Return chaineR
     
        End Function
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
        Public Shared Function formaterCaractereSpeciaux2(ByVal chaineR As String) As String
            Dim i As Integer
     
            Const CS = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåæçèéêëìíîïù"
            Const CN = "AAAAAAACEEEEIIIIDNOOOOOUUUUUaaaaaaaceeeeiiiiu"
            Const CR = "²&~#{([|`_\^@)]°}=+'^¨$£¤%µ*,?;:/!§<>"
     
     
            For i = 0 To CS.Length - 1
                If chaineR.Contains(CS(i)) Then
                    chaineR.Replace(CS(i), CN(i))
                End If
            Next
     
            If chaineR.IndexOfAny(CR) <> -1 Then
                For i = 0 To CR.Length - 1
                    chaineR = chaineR.Replace(CR(i), " ")
                Next
            End If
     
     
            Return chaineR
     
        End Function

  2. #2
    Modérateur
    Avatar de DotNetMatt
    Homme Profil pro
    CTO
    Inscrit en
    Février 2010
    Messages
    3 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : CTO
    Secteur : Finance

    Informations forums :
    Inscription : Février 2010
    Messages : 3 611
    Billets dans le blog
    3
    Par défaut
    Il y a une méthode assez connue qui permet de remplacer les caractères accentués :
    Code VB.NET : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Public Shared Function RemoveDiacritics(inputString As String) As String
    	'!\\ Warning 'œ' will be replaced with a 'o' not an 'oe'
    	Dim normalizedString As [String] = inputString.Normalize(NormalizationForm.FormD)
    	Dim stringBuilder As New StringBuilder()
    	For i As Integer = 0 To normalizedString.Length - 1
    		Dim c As [Char] = normalizedString(i)
    		If System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c) <> System.Globalization.UnicodeCategory.NonSpacingMark Then
    			stringBuilder.Append(c)
    		End If
    	Next
    	Return stringBuilder.ToString()
    End Function
    Ca ne gère pas les caractères que tu as mis dans la constante CR. Cependant pour les accents, c'est on ne peut plus efficace
    Less Is More
    Pensez à utiliser les boutons , et les balises code
    Desole pour l'absence d'accents, clavier US oblige
    Celui qui pense qu'un professionnel coute cher n'a aucune idee de ce que peut lui couter un incompetent.

  3. #3
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2011
    Messages
    117
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 117
    Par défaut
    Je dirais la première mais avec une seul boucle :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        Public Shared Function formaterCaractereSpeciaux(ByVal chaineR As String) As String
            Dim i As Integer
     
            Const CS = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåæçèéêëìíîïù²&~#{([|`_\^@)]°}=+^¨$£¤%µ*,?;:/!§<>"
            Const CN = "AAAAAAACEEEEIIIIDNOOOOOUUUUUaaaaaaaceeeeiiiiu                                    "
     
            For i = 0 To CN.Length - 1
                chaineR = chaineR.Replace(CS(i), CN(i))
            Next
     
            Return chaineR
     
        End Function
    Mais as tu pensais aux caractères spéciaux (© ☺☻♥♦♣♠•◘○....) ?

  4. #4
    Membre Expert
    Avatar de Sehnsucht
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Octobre 2008
    Messages
    847
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Lot et Garonne (Aquitaine)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Octobre 2008
    Messages : 847
    Par défaut
    Aucune des 2

    Ne jamais faire des Replace dans une boucle quand on se soucie des perfs, 'le nombre d'allocations de nouvelles chaines explose), StringBuilder sert à ça [et c'est vite fait d'oublier de ré-affecter le résultat du Replace comme dans la 1ère boucle du 2ème exemple]

    accessoirement donner de meilleurs noms aux constantes pourrait être un plus (bon là je chipote un peu plus )

    Donc pour résumer prendre le code de DotNetMatt pour l'utilisation du StringBuilder y adjoindre la logique exposée par Guyome41 et voilà

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Function RemoveSpecialChars(input As String) As String
        Const Diacritics = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåæçèéêëìíîïù²&~#{([|`_\^@)]°}=+^¨$£¤%µ*,?;:/!§<>"
        Const Normalized = "AAAAAAACEEEEIIIIDNOOOOOUUUUUaaaaaaaceeeeiiiiu                                    "
     
        ' TODO: test input null ?
        Dim builder As New StringBuilder(input.Length)
     
        For Each character In input
            Dim index = Diacritics.IndexOf(character)
     
            ' Could also use a classic If here
            builder.Append(If(index > -1, Normalized(index), character))
        Next
     
        Return builder.ToString
    End Function
    Pour les aficionados de Linq on peut faire un Aggregate si on veut:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Return input.Aggregate(
        New StringBuilder(input.Length),
        Function(builder, character)
            Dim index = Diacritics.IndexOf(character)
            Return builder.Append(If(index > -1, Normalized(index), character))
        End Function).ToString
    Ou utiliser conjointement String.Concat et un Select (String implémentant IEnumerable(Of Char)) (je mets les 2 syntaxes)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Return String.Concat(input.Select(Function(character)
                                          Dim index = Diacritics.IndexOf(character)
                                          Return If(index > -1, Normalized(index), character)
                                      End Function))
     
    Return String.Concat(From character In input
                         Let index = Diacritics.IndexOf(character)
                         Select If(index > -1, Normalized(index), character))
    Pas certains que les perfs soient meilleures ou pires, je suppose (avec la flemme de vérifier ) que String.Concat utilise en interne un StringBuilder.
    Il y aura possiblement un surcoût dû aux lambdas mais ça peut aussi être optimisé efficacement pour le compilo.

    Bref à benchmarker pour voir et si c'est sensiblement identique, préférer la syntaxe la plus lisible et maintenable pour l'équipe en place.

    Cordialement !

  5. #5
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 198
    Par défaut
    j'ai pas le temps de me pencher sur la question mais il y a des tas de possibilités, et le system.diagnostics.stopwatch pour tester (faire quelques millions d'itération pour voir une différence)

    par contre j'ai testé il y a peu une version avec strinbuilder et boucle et une version avec replace et boucle
    la version avec replace était pas loin de 1000x plus rapide

    moi je testerais bien if entree.tochararray.intersect(charsneedreplacement).count = 0 then return entree au début
    là aussi on peut tester si ca vaut le coup ou pas ...
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  6. #6
    Membre Expert
    Avatar de Sehnsucht
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Octobre 2008
    Messages
    847
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Lot et Garonne (Aquitaine)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Octobre 2008
    Messages : 847
    Par défaut
    Attention aux tests, selon ce que l'on teste on peut avoir des résultats bien différents

    Petit truc vite fait (c'est pas le code le plus propre )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    Module ReplacementTests
     
        Sub Main()
            Const loremPath = "loremIpsum.txt"
     
            Const iterations = 10000
            Const diacritics = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåæçèéêëìíîïù²&~#{([|`_\^@)]°}=+^¨$£¤%µ*,?;:/!§<>"
     
            ' Make sure all params are good (path, length)
            Dim longDiacritics = String.Concat(Enumerable.Repeat(New Random, iterations).Select(Function(rnd) diacritics(rnd.Next(diacritics.Length))))
            Dim loremIpsum = IO.File.ReadAllText(loremPath)
            Dim shortLorem = loremIpsum.Substring(0, diacritics.Length)
            Dim longLorem = loremIpsum.Substring(0, iterations)
     
            Dim samples As New Dictionary(Of String, String) From {
                {"Empty", String.Empty},
                {"All Diacritics Once", diacritics},
                {"Only Diacritics LongText", longDiacritics},
                {"Lorem Ipsum ShortText", shortLorem},
                {"Lorem Ipsum LongText", longLorem}
            }
     
            ' Call for nothing to load and let JIT do it's stuff
            ReplaceLoop(String.Empty)
            StringBuilderLoop(String.Empty)
     
            For Each sample In samples
                Console.WriteLine(sample.Key)
                Console.WriteLine("{0}{1} times ReplaceLoop in {2}", vbTab, iterations, Measure(Sub()
                                                                                                    For i = 1 To iterations
                                                                                                        ReplaceLoop(sample.Value)
                                                                                                    Next
                                                                                                End Sub))
                Console.WriteLine("{0}{1} times StringBuilderLoop in {2}", vbTab, iterations, Measure(Sub()
                                                                                                          For i = 1 To iterations
                                                                                                              StringBuilderLoop(sample.Value)
                                                                                                          Next
                                                                                                      End Sub))
            Next
     
        End Sub
     
        Function ReplaceLoop(input As String) As String
            Const Diacritics = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåæçèéêëìíîïù²&~#{([|`_\^@)]°}=+^¨$£¤%µ*,?;:/!§<>"
            Const Normalized = "AAAAAAACEEEEIIIIDNOOOOOUUUUUaaaaaaaceeeeiiiiu                                    "
     
            For i = 0 To Normalized.Length - 1
                input = input.Replace(Diacritics(i), Normalized(i))
            Next
     
            Return input
        End Function
     
        Function StringBuilderLoop(input As String) As String
            Const Diacritics = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝàáâãäåæçèéêëìíîïù²&~#{([|`_\^@)]°}=+^¨$£¤%µ*,?;:/!§<>"
            Const Normalized = "AAAAAAACEEEEIIIIDNOOOOOUUUUUaaaaaaaceeeeiiiiu                                    "
     
            ' TODO: test input null ?
            Dim builder As New Text.StringBuilder(input.Length)
     
            For Each character In input
                Dim index = Diacritics.IndexOf(character)
     
                ' Could also use a classic If here
                builder.Append(If(index > -1, Normalized(index), character))
            Next
     
            Return builder.ToString
        End Function
     
        Function Measure(body As Action) As TimeSpan
            Dim watch = Stopwatch.StartNew
            body()
            Return watch.Elapsed
        End Function
     
    End Module
    Pour le fichier c'est du généré via Lorem Ipsum
    Bien sûr testé en Release, voici ce que j'obtiens (les valeurs dépendent donc en partie de ma machine, c'est surtout pour l'ordre de grandeur )
    Code Console : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Empty
            10000 times ReplaceLoop in 00:00:00.0090664
            10000 times StringBuilderLoop in 00:00:00.0009644
    All Diacritics Once
            10000 times ReplaceLoop in 00:00:00.1936910
            10000 times StringBuilderLoop in 00:00:00.0568492
    Only Diacritics LongText
            10000 times ReplaceLoop in 00:00:15.6800618
            10000 times StringBuilderLoop in 00:00:06.3742968
    Lorem Ipsum ShortText
            10000 times ReplaceLoop in 00:00:00.0700978
            10000 times StringBuilderLoop in 00:00:00.0761711
    Lorem Ipsum LongText
            10000 times ReplaceLoop in 00:00:07.0591169
            10000 times StringBuilderLoop in 00:00:09.5916676

    J'ai aussi testé en rajoutant un test d'intersection en amont pour débrancher plus tôt mais les résultats étaient sensiblement les mêmes, a priori logique, faut parcourir toute la chaine pour savoir si au moins un existe)
    Code que j'avais rajouté: If input.IndexOfAny(Diacritics.ToCharArray) = -1 Then Return input

    Moralité plus la chaine contient de changements à faire plus StringBuilder sera performant face au Replace (principalement à cause de la réallocation d'une nouvelle chaine à chaque fois)

    Note: j'ai pas testé en utilisant StringBuilder.Replace (donc en instanciant StringBuilder avec le constructeur qui prend une chaine) peut-être que ça peut donner de bons résultats.

    Cordialement !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Remplacement du caractère ?
    Par Mvu dans le forum ASP
    Réponses: 9
    Dernier message: 06/01/2005, 18h11
  2. Réponses: 11
    Dernier message: 01/12/2004, 19h09
  3. [CR] Chercher et remplacer un caractère dans 1 chaine
    Par franck.cvitrans dans le forum SAP Crystal Reports
    Réponses: 2
    Dernier message: 29/09/2004, 16h42
  4. Fonction permettant de remplacer des caractères
    Par PrinceMaster77 dans le forum ASP
    Réponses: 3
    Dernier message: 06/09/2004, 15h48
  5. Remplacer un caractère
    Par Mvu dans le forum ASP
    Réponses: 5
    Dernier message: 20/07/2004, 09h57

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo