As long, as strings are immuteble, each manipulation with it, will cause GC load, even StringBuilder insert/remove calls.
I would cut source string by insertion points, and then "zip" it with data, that need to be inserted.
After that you can just concat strings inside list, to get resulting string.
Here is a sample code that do split/zip operaions.
It assumes, that Fields are defined as touple of (position, length, value).
public class Field
{
public int pos { get; set; }
public int len { get; set; }
public string value { get; set; }
public string tag { get; set; }
}
class Program
{
static void Main(string[] args)
{
var source = "You'r order price [price] and qty [qty].";
var fields = new List<Field>();
fields.Add(new Field()
{
pos = 18,
len = 7,
value = "15.99$",
tag = "price"
});
fields.Add(new Field()
{
pos = 37-3,
len = 5,
value = "7",
tag = "qty"
});
Console.WriteLine(Zip(Split(source, fields), fields));
Console.WriteLine(ReplaceRegex(source, fields));
}
static IEnumerable<string> Split(string source, IEnumerable<Field> fields)
{
var index = 0;
foreach (var field in fields.OrderBy(q => q.pos))
{
yield return source.Substring(index, field.pos - index);
index = field.pos + field.len;
}
yield return source.Substring(index, source.Length - index);
}
static string Zip(IEnumerable<string> splitted, IEnumerable<Field> fields)
{
var items = splitted.Zip(fields, (l, r) => new string[] { l, r.value }).SelectMany(q => q).ToList();
items.Add(splitted.Last());
return string.Concat(items);
}
static string ReplaceRegex(string source, IEnumerable<Field> fields)
{
var fieldsDict = fields.ToDictionary(q => q.tag);
var re = new Regex(@"\[(\w+)\]");
return re.Replace(source, new MatchEvaluator((m) => fieldsDict[m.Groups[1].Value].value));
}
}
BTW, would be better to replace special user markers, like [price], [qty] using regex?