Vérifier le contenu d'une chaîne ?Longueur de la chaîne par rapport à la chaîne vide
-
08-06-2019 - |
Question
Qu'est-ce qui est le plus efficace pour le compilateur et quelle est la meilleure pratique pour vérifier si une chaîne est vide ?
- Vérifier si la longueur de la chaîne == 0
- Vérifier si la chaîne est vide (strVar == "")
Aussi, la réponse dépend-elle de la langue ?
La solution
Oui, cela dépend de la langue, puisque le stockage des chaînes diffère selon les langues.
- Chaînes de type Pascal :
Length = 0
. - Chaînes de style C :
[0] == 0
. - .FILET:
.IsNullOrEmpty
.
Etc.
Autres conseils
Dans les langages qui utilisent des chaînes de style C (terminées par un caractère nul), par rapport à ""
sera plus rapide.C'est une opération O(1), alors que la longueur d'une chaîne de style C est O(n).
Dans les langages qui stockent la longueur dans le cadre de l'objet chaîne (C#, Java, ...), la vérification de la longueur est également O(1).Dans ce cas, vérifier directement la longueur est plus rapide, car cela évite la surcharge liée à la construction de la nouvelle chaîne vide.
Dans .Net :
string.IsNullOrEmpty( nystr );
les chaînes peuvent être nulles, donc .Length lève parfois une NullReferenceException
Dans les langages qui utilisent des chaînes de style C (terminées par un caractère nul), la comparaison avec "" sera plus rapide
En fait, il serait peut-être préférable de vérifier si le premier caractère de la chaîne est « \0 » :
char *mystring;
/* do something with the string */
if ((mystring != NULL) && (mystring[0] == '\0')) {
/* the string is empty */
}
En Perl, il existe une troisième option, à savoir que la chaîne n'est pas définie.C'est un peu différent d'un pointeur NULL en C, ne serait-ce que parce que vous n'obtenez pas d'erreur de segmentation pour accéder à une chaîne non définie.
En supposant que votre question soit .NET :
Si vous souhaitez également valider votre chaîne contre la nullité, utilisez IsNullOrEmpty, si vous savez déjà que votre chaîne n'est pas nulle, par exemple lors de la vérification de TextBox.Text etc., n'utilisez pas IsNullOrEmpty, et cela entre ensuite dans votre question.
Donc, à mon avis, String.Length est moins performant que la comparaison de chaînes.
Je l'ai testé (j'ai aussi testé avec C#, même résultat) :
Module Module1
Sub Main()
Dim myString = ""
Dim a, b, c, d As Long
Console.WriteLine("Way 1...")
a = Now.Ticks
For index = 0 To 10000000
Dim isEmpty = myString = ""
Next
b = Now.Ticks
Console.WriteLine("Way 2...")
c = Now.Ticks
For index = 0 To 10000000
Dim isEmpty = myString.Length = 0
Next
d = Now.Ticks
Dim way1 = b - a, way2 = d - c
Console.WriteLine("way 1 took {0} ticks", way1)
Console.WriteLine("way 2 took {0} ticks", way2)
Console.WriteLine("way 1 took {0} ticks more than way 2", way1 - way2)
Console.Read()
End Sub
End Module
Résultat:
Way 1...
Way 2...
way 1 took 624001 ticks
way 2 took 468001 ticks
way 1 took 156000 ticks more than way 2
Ce qui signifie que la comparaison nécessite bien plus que la vérification de la longueur des chaînes.
String.IsNullOrEmpty()
ne fonctionne que sur .net 2.0 et supérieur, pour .net 1/1.1, j'ai tendance à utiliser :
if (inputString == null || inputString == String.Empty)
{
// String is null or empty, do something clever here. Or just expload.
}
J'utilise String.Empty par opposition à "" parce que "" créera un objet, alors que String.Empty ne le fera pas - je sais que c'est quelque chose de petit et de trivial, mais je préfère quand même ne pas créer d'objets quand je n'en ai pas besoin !(Source)
En fait, selon l'OMI, le meilleur moyen de déterminer est la méthode IsNullOrEmpty() de la classe de chaîne.
http://msdn.microsoft.com/en-us/library/system.string.isnullorempty.
Mise à jour:J'ai supposé que .Net, dans d'autres langues, cela pourrait être différent.
Dans ce cas, vérifier directement la longueur est plus rapide, car cela évite la surcharge liée à la construction de la nouvelle chaîne vide.
@DerekPark :Ce n'est pas toujours vrai."" est une chaîne littérale donc, en Java, elle sera presque certainement déjà internée.
Pour les chaînes C,
if (s[0] == 0)
sera plus rapide que l'un ou l'autre
if (strlen(s) == 0)
ou
if (strcmp(s, "") == 0)
car vous éviterez la surcharge d’un appel de fonction.
@Nathan
En fait, il serait peut-être préférable de vérifier si le premier caractère de la chaîne est « \0 » :
J'ai failli le mentionner, mais j'ai fini par le laisser de côté, depuis que j'ai appelé strcmp()
avec la chaîne vide et la vérification directe du premier caractère de la chaîne sont tous deux O(1).En gros, vous payez simplement pour un appel de fonction supplémentaire, ce qui est assez bon marché.Si tu vraiment Cependant, vous avez besoin de la meilleure vitesse absolue, optez certainement pour une comparaison directe du premier caractère à 0.
Honnêtement, j'utilise toujours strlen() == 0
, parce que j'ai jamais j'ai écrit un programme où il s'agissait en fait d'un problème de performances mesurable, et je pense que c'est la manière la plus lisible d'exprimer le contrôle.
Encore une fois, sans connaître la langue, il est impossible de le savoir.
Je vous recommande cependant de choisir la technique qui fait le plus de sens pour le programmeur de maintenance qui suivra et qui devra entretenir votre travail.
Je recommanderais d'écrire une fonction qui fait explicitement ce que vous voulez, comme
#define IS_EMPTY(s) ((s)[0]==0)
ou comparable.Maintenant, il n'y a aucun doute sur le fait que vous vérifiez.
Après avoir lu ce fil de discussion, j’ai mené une petite expérience qui a donné deux résultats distincts et intéressants.
Considérer ce qui suit.
strInstallString "1" string
Ce qui précède est copié à partir de la fenêtre locale du débogueur Visual Studio.La même valeur est utilisée dans les trois exemples suivants.
if ( strInstallString == "" ) === if ( strInstallString == string.Empty )
Voici le code affiché dans la fenêtre de désassemblage du débogueur Visual Studio 2013 pour ces deux cas fondamentalement identiques.
if ( strInstallString == "" )
003126FB mov edx,dword ptr ds:[31B2184h]
00312701 mov ecx,dword ptr [ebp-50h]
00312704 call 59DEC0B0 ; On return, EAX = 0x00000000.
00312709 mov dword ptr [ebp-9Ch],eax
0031270F cmp dword ptr [ebp-9Ch],0
00312716 sete al
00312719 movzx eax,al
0031271C mov dword ptr [ebp-64h],eax
0031271F cmp dword ptr [ebp-64h],0
00312723 jne 00312750
if ( strInstallString == string.Empty )
00452443 mov edx,dword ptr ds:[3282184h]
00452449 mov ecx,dword ptr [ebp-50h]
0045244C call 59DEC0B0 ; On return, EAX = 0x00000000.
00452451 mov dword ptr [ebp-9Ch],eax
00452457 cmp dword ptr [ebp-9Ch],0
0045245E sete al
00452461 movzx eax,al
00452464 mov dword ptr [ebp-64h],eax
00452467 cmp dword ptr [ebp-64h],0
0045246B jne 00452498
if ( strInstallString == string.Empty ) n'est pas significativement différent
if ( strInstallString.Length == 0 )
003E284B mov ecx,dword ptr [ebp-50h]
003E284E cmp dword ptr [ecx],ecx
003E2850 call 5ACBC87E ; On return, EAX = 0x00000001.
003E2855 mov dword ptr [ebp-9Ch],eax
003E285B cmp dword ptr [ebp-9Ch],0
003E2862 setne al
003E2865 movzx eax,al
003E2868 mov dword ptr [ebp-64h],eax
003E286B cmp dword ptr [ebp-64h],0
003E286F jne 003E289C
À partir des listes de codes machine ci-dessus, générées par le module NGEN du .NET Framework, version 4.5, je tire les conclusions suivantes.
Les tests d'égalité par rapport à la chaîne littérale vide et à la propriété string.Empty statique de la classe System.string sont, à toutes fins pratiques, identiques.La seule différence entre les deux extraits de code est la source de la première instruction de déplacement, et les deux sont des décalages par rapport à ds, ce qui implique que les deux font référence à des constantes intégrées.
Le test d'égalité par rapport à la chaîne vide, en tant que littéral ou propriété string.Empty, configure un appel de fonction à deux arguments, qui indique inégalité en renvoyant zéro.Je base cette conclusion sur d'autres tests que j'ai effectués il y a quelques mois, dans lesquels j'ai suivi une partie de mon propre code à travers la division géré/non géré et inversement.Dans tous les cas, tout appel nécessitant deux arguments ou plus place le premier argument dans le registre ECX et le second dans le registre EDX.Je ne me souviens pas comment les arguments ultérieurs ont été adoptés.Néanmoins, la configuration de l'appel ressemblait plus à __fastcall qu'à __stdcall.De même, les valeurs de retour attendues apparaissaient toujours dans le registre EAX, qui est presque universel.
Tester la longueur de la chaîne définit un appel de fonction à un argument, qui renvoie 1 (dans le registre EAX), qui se trouve être la longueur de la chaîne testée.
Étant donné que le code machine immédiatement visible est presque identique, la seule raison que j'imagine qui expliquerait les meilleures performances de l'égalité des chaînes sur la longueur de la chaîne rapportée par Brillant est que la fonction à deux arguments qui effectue la comparaison est nettement mieux optimisée que la fonction à un argument qui lit la longueur de l'instance de chaîne.
Conclusion
Par principe, j'évite de comparer la chaîne vide en tant que littéral, car la chaîne vide littérale peut paraître ambiguë dans le code source.À cette fin, mes classes d'assistance .NET définissent depuis longtemps la chaîne vide comme une constante.Même si j'utilise chaîne.Vide pour les comparaisons directes en ligne, la constante gagne son argent pour définir d'autres constantes dont la valeur est la chaîne vide, car une constante ne peut pas être affectée chaîne.Vide comme sa valeur.
Cet exercice dissipe, une fois pour toutes, toute inquiétude que je pourrais avoir concernant le coût, le cas échéant, de la comparaison avec l'un ou l'autre chaîne.Vide ou la constante définie par mes classes d'assistance.
Cependant, cela soulève également une question déroutante : son remplacement ;pourquoi comparer avec chaîne.Vide plus efficace que de tester la longueur de la chaîne ?Ou le test utilisé par Shinny est-il invalidé à cause de la manière dont la boucle est implémentée ?(Je trouve cela difficile à croire, mais là encore, j'ai déjà été dupé, et je suis sûr que vous aussi !)
J'ai longtemps supposé que système.string les objets étaient des chaînes comptées, fondamentalement similaires à la chaîne de base (BSTR) établie de longue date que nous connaissons depuis longtemps grâce à COM.