5
\$\begingroup\$

I have had a problem with encoding in a project when trying to convert certain html code into image format. It happens when we receive html that has certain characters with accents and other non standard ones.

To combat this I have the following method which finds all of the text inside html tags and then converts all of it (that aren't already in the encoded format i.e.  ) to their html escaped representation i.e. + to +.

private string EncodeToHtml(string contents)
{
    Regex textRegex = new Regex("(?<!<[^>]*)(?<Text>[^<>]*)", RegexOptions.Compiled);
    Regex innerRegex = new Regex("(?<=^|;)([^&]|&(?=[^&;]*(?:&|$)))+", RegexOptions.Compiled);

    return textRegex.Replace(contents, new MatchEvaluator(m =>
    {
        return innerRegex.Replace(m.Groups["Text"].Value, new MatchEvaluator(m2 =>
        {
            string result = string.Empty;
            foreach (char c in m2.Value)
            {
                result += $"&#{(int)c};";
            }

            return result;
        }));
    }));
}

I'd appreciate any comments on the code especially anyway to make it more efficient.

\$\endgroup\$
7
  • \$\begingroup\$ HttpUtility.HtmlEncode Method :-) \$\endgroup\$
    – t3chb0t
    Commented Feb 16, 2017 at 17:21
  • \$\begingroup\$ @t3chb0t Characters like å are considered valid html and so aren't escaped using that method (I think I originally tried the WebUtility version from .NET 4.0 but it should have the same affect). That is why I rolled my own solution, it's not that it is invalid html, its that it causes problems for another part of my application. \$\endgroup\$ Commented Feb 16, 2017 at 17:23
  • \$\begingroup\$ @t3chb0t Either way I'd still have to use the first two regular expressions to extract the correct text to escape. \$\endgroup\$ Commented Feb 16, 2017 at 17:24
  • \$\begingroup\$ OK, just wanted to be sure you don't reinvent the wheel. \$\endgroup\$
    – t3chb0t
    Commented Feb 16, 2017 at 17:26
  • \$\begingroup\$ @t3chb0t You were right to check! \$\endgroup\$ Commented Feb 16, 2017 at 17:27

1 Answer 1

3
\$\begingroup\$

You can make the loop more efficient by using the StringBuilder and using the Aggregate extension:

return m2.Value.Aggregate(
    new StringBuilder(),
    (current, next) => current.Append($"&#{next};")
).ToString())

You don't need the new MatchEvaluator, just the m is OK:

textRegex.Replace(contents, m => ..

You can even compress it by removing all returns:

return textRegex.Replace(contents, m => 
    innerRegex.Replace(m.Groups["Text"].Value, m2 =>
        m2.Value.Aggregate(
            new StringBuilder(),
            (current, next) => current.Append($"&#{next};")
        ).ToString())
    );

Regex textRegex = ..  
Regex innerRegex = ..

I'm not sure about these two variables if they are not recompiled each time the method is called. It might be better to put them outside the method and make them static.


One final thought. You could split this method into three so that you can create finer tests and reuse any of them.

This method assembles both helpers to encode html.

public string EncodeToHtml(string contents)
{
    return ReplaceHtmlText(contents, m => EncodeText(m.Groups["Text"].Value));
}

This method just allows you to replace the text:

public string ReplaceHtmlText(string text, MatchEvaluator m)
{
    return textRegex.Replace(text, m);
}

This method allows you to encode the text:

public string EncodeText(string text)
{
    return encodeRegex.Replace(text, m =>
        m.Value.Aggregate(
            new StringBuilder(),
            (current, next) => current.Append($"&#{next};")
        ).ToString());
}
\$\endgroup\$
1
  • \$\begingroup\$ Side note: I did originally have the returns compressed like that, but I felt it harmed the readability. \$\endgroup\$ Commented Feb 17, 2017 at 8:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.