11

I have a function ReplaceParameters that replaces values in a string by using Regex.Replace. This has been working fine, but now the api that gets replacement-string has become async-only. This is a repro of the current code:

public static string ReplaceParameters(string query)
{
    var result = Regex.Replace(query, @"(?<parameter>\|\w+\|)", ReplaceParameterEvaluator,
                                         RegexOptions.ExplicitCapture);

    return result;
}

private static string ReplaceParameterEvaluator(Match parameterMatch)
{
    var parameter = parameterMatch.Groups["parameter"].Value;
    return GetReplacement(parameter);
}

private static string GetReplacement(string parameter)
{
    //...
}

Since the (new) GetReplacement function now is returning a Task instead of string: private static async Task<string> GetReplacementAsync(string parameter) the ReplaceParameterEvaluator function can't be made compatible with the MatchEvaluator delegate.

Sinc this has to run on a web-server and not cause dead-locks, i cant use any of the dirty async-to-sync hacks like for example this: (use .Result) var replacedQuery = Regex.Replace(query, @"(?<parameter>\|\w+\|)", match => ReplaceParameterEvaluatorAsync(match).Result, RegexOptions.ExplicitCapture);

Is it possible to rewrite the function to find all the texts, and then replace them? Could Regex.Matches be used in some way?

(Seriously though, why is there not a Regex.ReplaceAsync function??)

5
  • Either don't use GetReplacementAsync or use "hacks". No elegant solution here. Commented Oct 8, 2015 at 11:12
  • Right after posting i found osdn.jp/projects/opentween/scm/git/open-tween/blobs/master/… - Which contains a simple implementation for RegexAsync.ReplaceAsync. But it does not support capture-groups. Perhaps a simliar aproach could be used in my case? Commented Oct 8, 2015 at 11:18
  • It seems to first match everything, then find replacements asynchronously and then replace everything. It's using regex twice, but yes, it's probably possible. Commented Oct 8, 2015 at 11:27
  • 2
    There is no Regex.ReplaceAsync function because string replacement is not an I/O bound operation, and exposing an async API wouldn't make sense. Commented Oct 8, 2015 at 13:23
  • @LucasTrzesniewski it CAN be an I/O bound operation though, in my case it's replacing <img> tags in HTML with base64 encoded image data read from disk. So - many hanks for your helpful answer below! Commented Jul 23, 2023 at 18:01

1 Answer 1

22

It's simple enough to build your own extension method for this:

public static async Task<string> ReplaceAsync(this Regex regex, string input, Func<Match, Task<string>> replacementFn)
{
    var sb = new StringBuilder();
    var lastIndex = 0;

    foreach (Match match in regex.Matches(input))
    {
        sb.Append(input, lastIndex, match.Index - lastIndex)
          .Append(await replacementFn(match).ConfigureAwait(false));

        lastIndex = match.Index + match.Length;
    }

    sb.Append(input, lastIndex, input.Length - lastIndex);
    return sb.ToString();
}

It's straightforward:

  • Copy unmatched chunks of text as-is to a StringBuilder
  • Append the result of the callback function where text is matched

This is just a convenience method for calling async callbacks. It's not in the API because regex matching and replacement is a CPU-bound operation, which is naturally not asynchronous.

Sign up to request clarification or add additional context in comments.

7 Comments

You could potentially even parallelize all the calls to replacementFn and then build the string at the end.
It's been some years since I last used the regex-classes... As you see in the original code, I'm using Regex.Replace (The static version) - Is it possible to change my code to use a Regex instance instead?
new Regex(@"(?<parameter>\|\w+\|)", RegexOptions.ExplicitCapture).ReplaceAsync(...)
@Rawling I would be careful with that. What if replacementFn is not thread-safe?
Tanks! I have no problem with the regex not being cpu-bound. My function however is. - And you gave a nice workaround.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.