2012-12-10 4 views
2

J'écris une fente de chaîne personnalisée. Il se divise sur un point (.) qui n'est pas précédé d'un nombre impair de barres obliques inverses (\).fractionnement de chaîne personnalisé rapide

«string» -> «IEnemerable<string>» 
"hello.world" -> "hello", "world" 
"abc\.123" -> "abc\.123" 
"aoeui\\.dhtns" -> "aoeui\\","dhtns" 

Je voudrais savoir s'il y a une sous-chaîne qui réutiliser la chaîne d'origine (pour la vitesse), ou est-il une division existante qui peut le faire rapidement?


C'est ce que j'ai, mais est 2-3 fois plus lent que input.Split('.') // où l'entrée est une chaîne. (Je sais qu'il est un (problème un peu plus complexe, mais pas beaucoup)

public IEnumerable<string> HandMadeSplit(string input) 
    { 
     var Result = new LinkedList<string>(); 
     var word = new StringBuilder(); 
     foreach (var ch in input) 
     { 
      if (ch == '.') 
      { 
       Result.AddLast(word.ToString()); 
       word.Length = 0; 
      } 
      else 
      { 
       word.Append(ch); 
      } 
     } 
     Result.AddLast(word.ToString()); 
     return Result; 
    } 

Il utilise maintenant la liste au lieu de LinkedList, et commencer enregistrement et à la fin de la sous-chaîne et utiliser String.substring pour créer la nouveaux sous-chaînes. Cela fait beaucoup et est presque aussi rapide que string.split mais je l'ai ajouté mes ajustements. (ajoutera code)

+1

"s'il existe une sous-chaîne qui réutilisera la chaîne d'origine (pour la vitesse)" - vous ne pouvez pas vraiment l'implémenter si ce n'est pas le comportement de .NET. – millimoose

+1

allez-vous jamais entrer dans le cas: 'aeiou \ .bcde \\. Opz'? ou n'en auras-tu qu'un seul? dans ta chaîne? – Jastill

+0

plusieurs points (séparateurs), et parfois des points d'échappement \. (non séparateurs). Parfois \\ aussi bien. –

Répondre

2

La boucle que vous montrez est la bonne approche si vous avez besoin de performances. (Regex ne serait pas être).

Basculer vers une boucle for basée sur l'index. Rappelez-vous l'index du début du match. N'ajoutez pas de caractères individuels. Au lieu de cela, rappelez-vous la plage de caractères à copier et faites-le avec un seul appel Substring par article. En outre, n'utilisez pas un LinkedList. Il est plus lent qu'un List pour presque tous les cas sauf les mutations à accès aléatoire.

Vous pouvez également passer de List à un tableau normal que vous avez redimensionné avec Array.Resize.Il en résulte un code légèrement fastidieux (parce que vous avez inséré une partie de la classe List dans votre méthode) mais il supprime quelques petits surcoûts.

Ensuite, ne renvoyez pas un IEnumerable car cela force l'appelant par indirection lors de l'accès à ses éléments. Renvoie un List ou un tableau.

+0

Changed to List, cela fait un peu, mais accélère aussi le client (appelant) beaucoup. –

+1

+1 merci certaines de ces idées ont aidé –

1

vous ne devriez pas essayer d'utiliser string.Split pour cela.

Si vous avez besoin aider à le mettre en œuvre, un moyen simple de résoudre ce problème est d'avoir une boucle qui s peut contenir la chaîne, en gardant la trace du dernier endroit où vous avez trouvé un point de qualification. Lorsque vous trouvez un nouveau point qualificatif (ou atteignez la fin de la chaîne d'entrée), yield return la sous-chaîne actuelle.

Modifier: sur le retour d'une liste ou un tableau par rapport à l'aide d'un rendement

Si dans votre application, le plus important est le temps passé par l'appelant sur itérer les sous-chaînes, alors vous devriez remplir une liste ou un tableau et retourner cela, comme suggéré dans la question acceptée. Je n'utiliserais pas un tableau redimensionnable en collectant les sous-chaînes car cela serait lent. D'autre part, si vous vous souciez de la performance globale et de la mémoire, et si parfois l'appelant n'a pas à parcourir toute la liste, vous devez utiliser yield return. Lorsque vous utilisez yield return, vous avez l'avantage qu'aucun code ne s'exécute tant que l'appelant n'a pas appelé MoveNext (directement ou indirectement via un foreach). Cela signifie que vous sauvegardez la mémoire pour l'allocation du tableau/liste, et vous économisez le temps passé sur l'allocation/redimensionnement/remplissage de la liste. Vous passerez du temps presque uniquement sur la logique de trouver les sous-chaînes, et cela sera fait paresseusement, c'est-à-dire seulement quand vous en aurez vraiment besoin parce que l'appelant continuera à itérer les sous-chaînes.

+0

fait ça Mais j'ai besoin d'être rapide. –

+0

@richard: Sous string.Split() sera juste en boucle avec les overheads. L'idée de le faire vous-même permettra d'éliminer ces frais généraux et de le rendre plus rapide. – Ian

+0

+ 1 merci, certaines de ces idées ont aidé –

2

C'est celui que j'ai finalement choisi. Ce n'est pas aussi rapide qu'un string.split, mais assez bon et peut être modifié, pour faire ce que je veux.

private IEnumerable<string> HandMadeSplit2b(string input) 
    { 
     //this one is margenaly better that the second best 2, but makes the resolver (its client much faster), nealy as fast as original. 
     var Result = new List<string>(); 
     var begining = 0; 
     var len = input.Length; 
     for (var index=0;index<len;index++) 
     { 
      if (input[index] == '.') 
      { 
       Result.Add(input.Substring(begining,index-begining)); 
       begining = index+1; 
      } 
     } 
     Result.Add(input.Substring(begining)); 
     return Result; 
    } 
Questions connexes