Il est parfois nécessaire d’extraire une sous-chaîne délimitée par un séparateur. Je vous présente ici deux fonctions polyvalentes écrites en VBA.
Cahier des charges
De la chaîne « a-b-c-d-e-f » on souhaite extraire la sous-chaîne x délimitée par le séparateur « -« .
Le premier token correspond à « a », le troisième sera « c » et le dernier « f ».
Notre fonction doit pouvoir gérer les cas particuliers suivants :
- Si Chaîne = « a » le token n°1 sera « a »
- Si Chaîne = « a- » le token n°1 sera « a » et le token n°2 sera une chaîne vide
- Si Chaîne = « – » les tokens seront vides
- Si Chaîne = « » la fonction retourne une chaîne vide
- Si le numéro de token demandé est supérieur au nombre de token, la fonction retourne une chaîne vide
De plus, elle doit pouvoir gérer un séparateur composé d’un ou plusieurs caractères et proposer une comparaison binaire ou textuelle (insensible à la casse des caractères) sur le séparateur.
Première solution : Split()
Intuitivement, la première idée est d’utiliser la fonction VBA Split() qui découpe la chaîne initiale en fonction du séparateur.
'Retourne le token spécifié par son numéro
'Auteur : Philben - v1.0
'Exemple : TokenBySplit("a-b-c","-",2) -> b
Dim a() As String
If Texte <> vbNullString And Numero > 0 Then
a = Split(Texte, Separateur, Numero + 1, TypeComparaison)
If UBound(a) >= Numero - 1 Then
TokenBySplit = a(Numero - 1)
ElseIf Numero = 1 Then
TokenBySplit = Texte
End If
End If
End Function
Deuxième solution
L’idée était de s’affranchir de Split() qui n’est pas reconnu pour sa vitesse d’exécution…
'Retourne le token spécifié par son numéro
'Auteur : Philben - v1.01
'Exemple : Token("a-b-c","-",2) -> b
Dim Tok As String
Dim x As Long, y As Long
Select Case Numero
Case Is > 1
x = InStr(1, Replace(texte, Separateur, vbNullString, 1, Numero - 2, TypeComparaison), Separateur, TypeComparaison)
If x > 0 Then
x = x + Len(Separateur) * (Numero - 1)
y = InStr(x, texte, Separateur, TypeComparaison)
If y > 0 Then
Tok = Mid$(texte, x, y - x)
Else
Tok = Mid$(texte, x)
End If
End If
Case 1
x = InStr(1, texte, Separateur, TypeComparaison)
If x > 0 Then
Tok = Left$(texte, x - 1)
Else
Tok = texte
End If
Case -1
x = InStrRev(texte, Separateur, -1, TypeComparaison)
If x > 0 Then
Tok = Mid$(texte, x + Len(Separateur))
Else
Tok = texte
End If
End Select
Token = Tok
End Function
Le principe est de supprimer par Replace() les séparateurs qui précèdent celui que l’on recherche par InStr().
Si on a besoin de rechercher le dernier token sans connaître sa position, la fonction le permet en passant -1 dans le numéro du token recherché.
Qualité
Pour vérifier que les fonctions répondent bien au cahier des charges, la fonction suivante réalise 30 tests.
Certains tests ne sont pas réalisés pour TokenBySplit() car elle ne gère pas le cas où le Numéro du token passé en paramètre est égal à -1 (recherche du dernier token sans connaître sa position).
Les résultats s’affichent dans la fenêtre d’exécution de VBE.
'Choix de la fonction à tester (True ou False)
Const TestTokenBySplit As Boolean = False
Dim a As Variant
Dim r As String, p As String
Dim i As Long, baseNumTest As Long
Dim bNoTest As Boolean
Debug.Print "*** Test de la fonction" & IIf(TestTokenBySplit, " TokenBySplit() ", " Token() ") & "***"
Debug.Print "Test n°", "Résultat", "Attendu", "Obtenu", "Paramètres"
a = Array("", "-", -1, vbBinaryCompare, "", "", "-", 1, vbBinaryCompare, "", "", "-", 2, vbBinaryCompare, "", _
"a", "-", -1, vbBinaryCompare, "a", "a", "-", 1, vbBinaryCompare, "a", "a", "-", 2, vbBinaryCompare, "", _
"a-", "-", -1, vbBinaryCompare, "", "a-", "-", 1, vbBinaryCompare, "a", "a-", "-", 2, vbBinaryCompare, "", _
"aa-", "-", 1, vbBinaryCompare, "aa", "-", "-", -1, vbBinaryCompare, "", "-", "-", 1, vbBinaryCompare, "", _
"-", "-", 2, vbBinaryCompare, "", "-b", "-", 1, vbBinaryCompare, "", "-b", "-", 2, vbBinaryCompare, "b", _
"-b", "-", 3, vbBinaryCompare, "", "a-b-", "-", -1, vbBinaryCompare, "", "a-b-", "-", 2, vbBinaryCompare, "b", _
"a-b-", "-", 3, vbBinaryCompare, "", "a-bb-", "-", 2, vbBinaryCompare, "bb", "a-b-c", "-", -1, vbBinaryCompare, "c", _
"a-b-c", "-", 3, vbBinaryCompare, "c", "a-b-c", "-", 4, vbBinaryCompare, "", "a:/bb", ":/", 1, vbBinaryCompare, "a", _
"a:/bb", ":/", 2, vbBinaryCompare, "bb", "a:/bb", ":/", -1, vbBinaryCompare, "bb", "aib", "I", 1, vbBinaryCompare, "aib", _
"aib", "I", 2, vbBinaryCompare, "", "aib", "I", 1, vbTextCompare, "a", "aib", "I", 2, vbTextCompare, "b")
baseNumTest = 1 - LBound(a)
For i = LBound(a) To UBound(a) Step 5
If TestTokenBySplit Then
'TokenBySplit : on retire les tests si Numero = -1 (dernier token) car ne gère pas ce cas
If a(i + 2) <> -1 Then
r = TokenBySplit(a(i), a(i + 1), a(i + 2), a(i + 3))
bNoTest = False
Else
bNoTest = True
End If
Else
r = Token(a(i), a(i + 1), a(i + 2), a(i + 3))
End If
p = "'" & a(i) & "','" & a(i + 1) & "'," & a(i + 2) & "," & a(i + 3)
If bNoTest = False Then
If StrComp(r, a(i + 4), vbBinaryCompare) = 0 Then
Debug.Print i / 5 + baseNumTest, "OK", "'" & a(i + 4) & "'", "'" & r & "'", p
Else
Debug.Print i / 5 + baseNumTest, "ERREUR", "'" & a(i + 4) & "'", "'" & r & "'", p
End If
Else
Debug.Print i / 5 + baseNumTest, "NON TESTE", , , p
End If
Next i
Debug.Print "*** Fin des tests ***"
End Function
Performances
La deuxième fonction est dans tous les cas plus performante que la première.
La différence peut aller jusqu’à 40% si on effectue une comparaison binaire sur le séparateur.
Exemples
Vous pouvez tester la fonction Token() avec ces exemples (coller les lignes dans la fenêtre Exécution de VBE, placer le curseur en fin d’une ligne à tester et valider par la touche « Entrée ») :
?token("c:\test\Fichiers\monfichier.txt",".",-1)
'Retourne le nom complet du fichier 'monfichier.txt'
?token("c:\test\Fichiers\monfichier.txt","\",-1)
'Retourne le nom du premier répertoire : 'test'
?token("c:\test\Fichiers\monfichier.txt","\",2)
'Retourne le drive : 'c:'
?token("c:\test\Fichiers\monfichier.txt","\",1)
'Retourne la lettre du drive : 'c'
?token("c:\test\Fichiers\monfichier.txt",":\",1)
Rappel
Pour connaître le nombre d’occurrences du séparateur, vous pouvez utiliser la fonction suivante :
If texte <> vbNullString Then CompteOccurrences = UBound(Split(texte, Chaine, -1, TypeComparaison))
End Function
Le nombre de tokens sera égal au nombre de séparateurs + 1
@+
Philippe