0

Just for clarity before I start my ask, I have sample C-code that I'm trying to modify with C# regular expressions. I'm not asking a question about C, I'm just using C# to automatically generate a C-file using regexes.

I'm trying to write a regex that'll replace a substring between two matching strings (tags). I followed along with this question, but I think I'm failing because my "tags" take the form of C-style block comments (which have backslashes and asterisks which are special characters in regex). Ultimately this will be used to automate the replacement of certain values in a C source file.

This is an example of some of my C-code:

SetKeyString("modelNumber", /* #ModelNumber#*/ config.modelNumber /*#ModelNumber#*/);
config.maxKV = /*#MaxKV#*/  88.88 /*#MaxKV#*/;  

I want to replace config.modelNumber and 88.88 with new values obtained externally from an XML file.

Suppose the data from my XML file is:

<ModelNumber>ABCDE</ModelNumber>
<MaxKV>99.99</MaxKV>

The resulting C-code should be

SetKeyString("modelNumber", /*#ModelNumber#*/ ABCDE /*#ModelNumber#*/);
config.maxKV = /*#MaxKV#*/ 99.99 /*#MaxKV#*/;   

This is the Regex I'm currently using to attempt (and miserably fail) at this.

string x = Regex.Replace(mainLines[i], @String.Format(@"?<=/*#{0}#*/)(\w+?)(?=/*#{0}#*/)", property.Name), "middle");

mainLines are the individual lines of my C-file and property.Name is the name of the XML tag: ModelNumber or MaxKV (without any characters on the ends).


Update - Additional examples

During further testing of proposed solutions failure edge cases were found so here are additional example inputs which caused failures:

    config.kvRampRate =         /*#KVRampRate#*/ (10.0 / config.maxKV * 4095) / 12.124567719929887 /*#KVRampRate#*/;
    config.maRampRate =     /*#MARampRate#*/ 1.0/config.maxMA * 4095 / /*mARampRate-->*/87.80017152658661 /*#MARampRate#*/;
3
  • 1
    You can escape special characters with backslashes in the regex: \*. You don't want a sequence of zero or more slashes, which is what you're trying to match: /*. You want a slash followed by an asterisk: /\*. Commented May 9, 2019 at 19:47
  • This is not a direct answer to your question, but this is what I use when I am making regex statements. It makes it super simple to make most regex statements that you could want. txt2re.com/index-csharp.php3 Commented May 9, 2019 at 20:12
  • The slash '/' is not a special character in C# regex, but the star '*' is (in all regex flavors). Commented May 9, 2019 at 20:33

1 Answer 1

1

I noted you had white-spacing issues in your example and escaping characters in your regular expression, this might be a couple of causes of your issues.

So for the specific example of "ModelNumber":

Regular Expression

(?<=/\*\s*(#ModelNumber#)\s*\*\/)(.+)(?=/\*\s*\k<1>\s*\*/)

Use

resultString = Regex.Replace(subjectString, @"(?<=/\*\s*(#ModelNumber#)\s*\*\/)(.+)(?=/\*\s*\k<1>\s*\*/)", " new value ");

Visualisation

Regex Visualisation

Regular Expression detailed description

  • Assert that the regex below can be matched backwards at this position (positive lookbehind) (?<=/\*\s*(#ModelNumber#)\s*\*\/)
    • Match the character “/” literally /
    • Match the character “*” literally \*
    • Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) \s*
      • Between zero and unlimited times, as many times as possible, giving back as needed (greedy) *
    • Match the regex below and capture its match into backreference number 1 (#ModelNumber#)
      • Match the character string “#ModelNumber#” literally (case sensitive) #ModelNumber#
    • Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) \s*
      • Between zero and unlimited times, as many times as possible, giving back as needed (greedy) *
    • Match the character “*” literally \*
    • Match the character “/” literally \/
  • Match the regex below and capture its match into backreference number 2 (.+)
    • Match any single character that is NOT a line break character (line feed) .+
      • Between one and unlimited times, as many times as possible, giving back as needed (greedy) +
  • Assert that the regex below can be matched starting at this position (positive lookahead) (?=/\*\s*\k<1>\s*\*/)
    • Match the character “/” literally /
    • Match the character “*” literally \*
    • Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) \s*
      • Between zero and unlimited times, as many times as possible, giving back as needed (greedy) *
    • Match the same text that was most recently matched by capturing group number 1 (case sensitive; fail if the group did not participate in the match so far) \k<1>
    • Match a single character that is a “whitespace character” (any Unicode separator, tab, line feed, carriage return, vertical tab, form feed, next line) \s*
      • Between zero and unlimited times, as many times as possible, giving back as needed (greedy) *
    • Match the character “*” literally \*
    • Match the character “/” literally /

Notes

  1. I'll leave the use of @String.Format to you
  2. You might want to add a space either side of your replacement value to keep spacing between your "tags" and the replacement string. Whilst the previous version of my answer handled this it hand a performance and didn't cover your edge cases easily.
  3. Remember to "regex escape" the "@String.Format" replacement value if there is a chance of it containing "regex" like values.
  4. This uses a "back reference" to only have one part to format / replace, that's the \k<1> part.
  5. Because this regular expression must capture something for the replacement to work you must have at least one character between your tags (a single space will do).

So this will work:

/*#ModelNumber#*/ /*#ModelNumber#*/

And this will not:

/*#ModelNumber#*//*#ModelNumber#*/

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

3 Comments

Dean, that worked great for most things, but it doesn't seem to work when there's forward slashes in the expression itself. I've modified my question to illustrate.
@audiFanatic I've added your examples I found in an non-approved question edit, I'll attempt to update my answer to reflect additional failure cases you provided.
@audiFanatic updated alternative and added more notes.

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.