简体   繁体   中英

What is the best practice to resolve placeholders in a plain text?

I need to resolve a huge load of placeholders (about 250) in a plain text.

A placeholder is defined as %ThisIsAPlaceholder% , an example would be %EmailSender% .

Now it's gets a bit creepy: the code should handle case insensitive placeholders too. So, %EmailSender% , %EMAILSENDER% and %emailsender% are the same placeholder. I think that's where it gets complicated.

My first approach was the something like:

public string ResolvePlaceholders(string text)
{
    var placeholders = new IEnumerable<string>
    {
        "%EmailSender%",
        "%ErrorMessage%",
        "%ActiveUser%"
    };

    var resolvedText = text;

    foreach(var placeholder in placeholders)
    {
        if(!replacedText.Contains(placeholder))
            continue;

        var value = GetValueByPlaceholder(placeholder);

        resolvedText = resolvedText.Replace(placeholder, value);
    }


    return resolvedText;
}

But.. as you may notice, i can't handle case insesitive placeholders. Also i check for every placeholder (if it is used in the text). When using > 200 placholders in a text with about 10'000 words i think this solution is not very fast.

How can this be solved in a better way? A solution that supports case insensitive placeholders would be appreciated.

A really basic but efficient replacement scheme for your case would be something like this:

private readonly static Regex regex = new Regex("%(?<name>.+?)%");

private static string Replace(string input, ISet<string> replacements)
{
    string result = regex.Replace(input, m => {

        string name = m.Groups["name"].Value;
        string value;
        if (replacements.Contains(name))
        {
            return GetValueByPlaceholder(name);
        }
        else   
        {
            return m.Captures[0].Value;
        }
    });

    return result;
}

public static void Main(string[] args)
{
    var replacements = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase)
    {
        "EmailSender", "ErrorMessage", "ActiveUser"
    };

    string text = "Hello %ACTIVEUSER%, There is a message from %emailsender%. %errorMessage%";
    string result = Replace(text, replacements);

    Console.WriteLine(result);
}

It will use a regular expression to go through the input text once. Note that we are getting case-insensitive comparisons via the equality comparer passed to the HashSet that we constructed in Main . Any unrecognized items will be ignored. For more general cases, the Replace method could take a dictionary:

private static string Replace(string input, IDictionary<string, string> replacements)
{
    string result = regex.Replace(input, m => {

        string name = m.Groups["name"].Value;
        string value;
        if (replacements.TryGetValue(name, out value))
        {
            return value;
        }
        else   
        {
            return m.Captures[0].Value;
        }
    });

    return result;
}

A typical recommendation when matching using quantifiers on input from an untrusted source (eg users over the internet) is to specify a match timeout for the regular expression. You would have to catch the RegexMatchTimeoutException that is thrown and do something in that case.

Regex solution

private static string ReplaceCaseInsensitive(string input, string search, string replacement)
{
    string result = Regex.Replace(
        input,
        Regex.Escape(search), 
        replacement.Replace("$","$$"), 
        RegexOptions.IgnoreCase
    );
    return result;
}

Non regex solution

public static string Replace(this string str, string old, string @new, StringComparison comparison)
{
    @new = @new ?? "";
    if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(old) || old.Equals(@new, comparison))
    return str;
    int foundAt;
    while ((foundAt = str.IndexOf(old, 0, StringComparison.CurrentCultureIgnoreCase)) != -1)
    str = str.Remove(foundAt, old.Length).Insert(foundAt, @new);
    return str;
}

Seems like a duplicate question / answer String.Replace ignoring case

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM