Menu

StringBuilder en C# : Append, Insert, Replace et quand l'utiliser

Pourquoi la concaténation répétée de chaînes dans une boucle est lente en C#, et comment StringBuilder y remédie : Append, AppendLine, AppendFormat, Insert, Remove, Replace, la capacité, et quand la concaténation simple ou string.Join est le meilleur choix.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Les chaînes sont immuables en C# : s += "x" n'étend pas s, il crée une nouvelle chaîne contenant les anciens caractères plus les nouveaux, et l'ancienne chaîne devient un déchet pour le ramasse-miettes. Une fois, ce n'est rien. Dans une boucle qui tourne des milliers de fois, chaque itération recopie tout ce qui a été construit jusque-là, donc le travail total croît avec le carré de la longueur.

StringBuilder, dans System.Text, est un tampon modifiable. Append écrit dans l'espace libre en fin de tampon, en agrandissant le tampon de temps en temps, et ToString() crée une seule chaîne à la fin.

Sortie :

Order #1042, total 59.97
Status: shipped
**********
51

Pourquoi la concaténation dans une boucle est lente

Voici le même texte construit des deux façons. Les temps varient selon la machine, les chiffres ci-dessous ne sont donc qu'un exemple ; c'est l'écart qui compte, et il se creuse à mesure que la boucle grandit :

Exemple de sortie :

string +=     482 ms, length 188890
StringBuilder 1 ms, length 188890

Avec 20 000 ajouts, la version += copie au total près de 2 milliards de caractères. StringBuilder copie chaque caractère environ deux fois, une fois à l'ajout et une fois dans ToString(), c'est pourquoi il reste autour d'une milliseconde.

Append, AppendLine et AppendFormat

Append a des surcharges pour chaque type intégré et renvoie le même builder, ce qui permet d'enchaîner les appels. AppendLine ajoute un saut de ligne après son argument. AppendFormat prend une chaîne de format composite comme string.Format :

Sortie :

RECEIPT
-------
Espresso        2.40
Croissant       3.10
Orange juice    4.50
TOTAL          10.00

Ajouter une chaîne interpolée (sb.Append($"...")) convient très bien et se lit facilement. Depuis .NET 6, le compilateur écrit même les morceaux directement dans le builder sans créer la chaîne intermédiaire.

AppendLine utilise Environment.NewLine : \n sous Linux et macOS, \r\n sous Windows. Quand un format de fichier exige une fin de ligne particulière, ajoutez-la vous-même.

Insert, Remove, Replace et l'indexeur

Contrairement à une chaîne, un StringBuilder peut être modifié sur place :

Sortie :

Hello, World
Hello, Maya
Maya
maya
maya!
[] length 0

Affecter une valeur plus petite à Length tronque ; c'est le moyen le moins coûteux de retirer un séparateur final. Clear() remet la longueur à 0 et conserve le tampon alloué, si bien qu'un même builder peut être réutilisé à chaque itération d'une boucle externe.

L'indexeur est rapide pour les ajouts récents, mais peut être lent pour un accès aléatoire dans un très grand builder, car le contenu est stocké en blocs chaînés. Si vous devez lire des caractères, appelez ToString() une fois et indexez la chaîne.

Le problème du séparateur final

Un usage classique est la construction d'une liste séparée par des virgules. Ajouter un séparateur après chaque élément en laisse un de trop à la fin :

Sortie :

csharp, dotnet, linq
csharp, dotnet, linq

Quand les éléments sont déjà dans une collection, string.Join est plus court et au moins aussi rapide. Gardez StringBuilder pour le texte dont la forme dépend de la logique : sections conditionnelles, boucles imbriquées, formatage mixte.

La capacité

Un StringBuilder commence avec de la place pour 16 caractères. Quand il en manque, il chaîne un nouveau bloc de stockage aussi grand que tout ce qu'il contient déjà, jusqu'à 8 000 caractères par bloc : sa capacité double tant qu'il est petit, puis augmente par paliers de 8 000 caractères. L'agrandissement ne recopie jamais le texte déjà stocké. Si vous connaissez à peu près la longueur du résultat, passez la capacité au constructeur et le builder n'aura jamais besoin de grandir :

Sortie :

12000
10000
True
True

Le dimensionnement préalable est une optimisation, pas une obligation. L'agrandissement alloue un nouveau bloc mais ne déplace jamais le texte existant, donc la valeur par défaut suffit, sauf si vous construisez beaucoup de grandes chaînes dans un chemin critique.

Quand ne pas utiliser StringBuilder

StringBuilder ne remplace pas avantageusement chaque +. Le compilateur transforme déjà a + b + c + d dans une seule expression en un appel à string.Concat, qui calcule la longueur finale et copie chaque morceau une fois, soit le même travail qu'un builder avec plus de code. Utilisez des chaînes simples quand :

  • Le nombre de morceaux est petit et fixe : $"{first} {last} ({age})".
  • Les morceaux sont dans une collection et ont besoin d'un séparateur : string.Join(", ", items).
  • Les morceaux n'ont pas besoin de séparateur : string.Concat(parts).
  • Vous concaténez quelques fois en dehors de toute boucle.

Utilisez StringBuilder quand vous ajoutez dans une boucle, un nombre de fois inconnu à l'avance, ou quand vous modifiez le texte (insertion, remplacement, suppression) pendant sa construction. Faire des allers-retours (sb.ToString() dans la boucle) annule le bénéfice ; appelez-le une seule fois à la fin.

Les flux sont l'autre option : pour produire un gros fichier, écrivez les lignes dans un StreamWriter au fur et à mesure au lieu de construire tout le contenu en mémoire. Voir fichiers.

Questions fréquentes

Qu'est-ce que StringBuilder en C# ?

System.Text.StringBuilder est un tampon de caractères modifiable qui sert à construire une chaîne en plusieurs étapes. Append ajoute à la fin du tampon au lieu de créer une nouvelle chaîne à chaque fois, et ToString() produit la chaîne finale une seule fois. C'est l'outil standard pour construire du texte dans une boucle.

Quand utiliser StringBuilder plutôt que la concaténation de chaînes ?

Quand vous ajoutez dans une boucle ou un nombre inconnu de fois, par exemple pour construire un rapport, un fichier CSV ou du HTML à partir d'une collection. Pour quelques morceaux fixes (a + b + c, ou une seule chaîne interpolée), la concaténation simple est aussi rapide et plus lisible, car le compilateur la combine déjà en un seul appel à string.Concat.

Comment supprimer le dernier caractère d'un StringBuilder ?

Réduisez sa Length : if (sb.Length > 0) sb.Length--; supprime le dernier caractère sans copie. sb.Remove(sb.Length - 1, 1) fait la même chose. C'est la façon habituelle de retirer une virgule finale, même si string.Join évite d'emblée le séparateur final.

StringBuilder est-il thread-safe ?

Non. Appeler Append sur le même StringBuilder depuis plusieurs threads à la fois peut corrompre son contenu ou lever une exception. Donnez à chaque thread son propre builder et combinez les résultats, ou protégez le builder partagé avec un lock.

Qu'ajoute AppendLine en C# ?

Le texte suivi de Environment.NewLine, qui vaut \r\n sous Windows et \n sous Linux et macOS. Si la sortie doit utiliser une fin de ligne précise, comme \n pour un format de fichier ou \r\n pour un en-tête HTTP, ajoutez-la explicitement avec Append("\n").

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER