views:

689

answers:

2

I need to find in a text everything that starts with [ and ends with ] and replace it with the value that a function returns. So, here is an example of what I'm doing:

    public string ProcessTemplate(string input)
    {
        return Regex.Replace(input, @"\[(.*?)\]", new MatchEvaluator(delegate(Match match)
            {
                return ProcessBlock(match.Result("$1"));
            }));
    }

    public string ProcessBlock(string value)
    {
        return Block.FromString(value).Process();
    }

Now, my problem is when I need to edit blocks. So I thought to find blocks, edit them, and then replacing them in the text.

So, I created a List of blocks, and separated the ProcessTemplate method to two methods: FindBlocks and ReplaceBlocks:

    public void FindBlocks(string input)
    {
        Input = input;

        foreach (Match match in Regex.Matches(input, @"\[(.*?)\]"))
            Blocks.Add(Block.FromString(match.Result("$1")));
    }

    public string ReplaceBlocks()
    {
        string input = Input;

        foreach (Block block in Blocks)
            input = input.Replace("[" + block.OrginalText + "]", block.Process);

        return input;
    }

    public IList<Block> Blocks
    {
        get;
        set;
    }

    public string Input
    {
        get;
        set;
    }

It's working, but the problem is that it is pretty slow. I measured with System.Diagnostics.Stopwatch every part, and I found out that the String.Replace in the ReplaceBlocks method is pretty slow.

How can I improve it?

Thanks.

A: 

I think it is slow

Don't optimize until you've profiled. Find out why your code is slow, then optimize those parts.

http://c2.com/cgi/wiki?ProfileBeforeOptimizing

kevingessner
See my edit please.
TTT
Great! One thing that may be faster is to store the character offsets of each block when you originally match it. Then replace that character range via Substring, rather than using string.Replace - should be faster. Of course, if your replaced content is not the same length as your original content, you'll have to update your offsets with that difference after each replacement. More bookkeeping, but may be faster in the end.
kevingessner
@kevingessner: I tried: "input = input.Substring(0, block.Position) + block.Process() + input.Substring(block.Position + block.Length);" but it's not working... block.Position = match.Index and block.Length = match.Length. Do you know why it's not working?
TTT
A: 

replacing the string in ReplaceBlock with a StringBuilder may provide a performance increase as every time you perform a string.replace it will have to deallocate the string and reallocate the string. string builder doesnt need to do this.

Replace the contents of the ReplaceBlock with the following.

// This will require a reference to System.Text
StringBuilder input =new StringBuilder(Input);
  foreach (Block block in Blocks)
  {
    input = input.Replace("[" + block.OrginalText + "]", block.Process);
  }
  return input.ToString();

I have also found replacing the foreach loops with a for loop which are quicker.

Mekboy