0

Due to an unfortunate past choice I have a following format string that looks like this:

# {0}mm {1}mm x {2}mm

It's processed in two stages, first one was to replace # character with a string with simple

formatString = formatString.Replace("#", "foo")

Somehow the original string changed to include octothorpes inside the format string as:

# {0}mm {1:0.##}mm x {2:0.##}mm

Because I still have to replace the first one # with foo, but the string.Replace("#","foo") will replace all occurences of #, resulting in foo {0}mm {1:0foofoo}mm x {2:0.foofoo}mm"

That's why I'd like to replace # symbol only if it's not inside curly braces. I think it can be achieved by a regex, but the correct expression doesn't come to my mind.

Axarydax
  • 16,353
  • 21
  • 92
  • 151

4 Answers4

4

This should work:

(?<!{[^{}]*)(?![^{}]*})#

Which will only match if:

  1. It is not preceded by an opening brace not followed by other braces,
  2. It is not followed by a closing brace not preceded by other braces,
  3. It is a hash.

The first two ensure you are not inside a brace pair, but not matching from one pair of braces to the other.

However if the hash you wish to match only occurs at the start then AlexInTime's solution is a much better approach.

Community
  • 1
  • 1
Richard
  • 106,783
  • 21
  • 203
  • 265
  • which part of the expression says 'It is not followed'? – Axarydax Jun 17 '14 at 07:36
  • @Axarydax The `(?!...)` is a ["Zero-Width Negative Lookahead Assertion"](http://msdn.microsoft.com/en-us/library/bs2twtah.aspx#zerowidth_negative_lookahead_assertion). – Richard Jun 17 '14 at 07:39
1

I know you already have an accepted answer, but this question also happens to have a solution that is quite general and beautiful, so for completion, here it is. This situation is very similar to this question to "regex-match a pattern, excluding..."

We can solve it with a beautifully-simple regex:

{[^}]*}|(#+)

The left side of the alternation | matches complete {curlies}. We will ignore these matches. The right side matches and captures hashes # to Group 1, and we know they are the right ones because they were not matched by the expression on the left.

This program shows how to use the regex (see the results at the bottom of the online demo):

using System;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
class Program
{
static void Main() {
var myRegex = new Regex(@"{[^}]*}|(#+)");
string s1 = @"# {0}mm ####{1:0.##}mm ##x {2:0.##}mm";

string replaced = myRegex.Replace(s1, delegate(Match m) {
if (m.Groups[1].Value != "") return "";
else return m.Value;
});
Console.WriteLine("\n" + "*** Replacements ***");
Console.WriteLine(replaced);


Console.WriteLine("\nPress Any Key to Exit.");
Console.ReadKey();

} // END Main
} // END Program

Reference

Community
  • 1
  • 1
zx81
  • 41,100
  • 9
  • 89
  • 105
  • I did think about this approach (when my first couple of lookahead tests failed :-)) it certainly adds complexity. While powerful the use of a callback delegate (or, to be more idiomatic, a lambda) is considerably more complex; and, in this case, feels wrong (IMHO) because the callback is only really there to do some additional pattern matching that can be done in the regex itself (splitting the problem across two tools). PS. the code would be clearer (and not need the ugly "END" comments) if indented. – Richard Jun 17 '14 at 09:45
  • @Richard When you say the lambda does more pattern matching... `if (m.Groups[1].Value != "")` is not really what I call pattern matching! lol... I do agree with you that there are pros and cons. One huge benefit of this method is that if next Tuesday we decide to also exclude hashes inside any `<##tag>`, it's extremely easy to implement and maintain! (Just add this other piece of alternation `<[^>]*>|` on the left of the existing regex.) But try to do that with other methods... It's another story. I love all good methods and just think this one deserves a place here. Cheers! :) – zx81 Jun 17 '14 at 09:53
0

your regex can be as simple as ^(#) ^ start of string, (#) one occurence of #

if you don't really know regex to much I can definitely recommed http://txt2re.com/ which is a VERY visual regex builder

VisualBean
  • 4,908
  • 2
  • 28
  • 57
-1

Use the Regex.Replace in stead of String.Replace

See http://msdn.microsoft.com/en-us/library/haekbhys(v=vs.110).aspx

Sample

using System.Text.RegularExpressions;
namespace TestReplaceFirst
{
    class Program
    {
        static void Main(string[] args)
        {
        var source = new List<string> { "# {0}mm {1}mm x {2}mm", "# {0}mm {1:0.##}mm x {2:0.##}mm", "{0}mm {1}mm x {2}mm # #", "{0}mm # {1:0.##}mm x {2:0.##}mm" };

        string pattern = "(?<!{[^{}]*)(?![^{}]*})#";
        string replacement = "foo";
        Regex rgx = new Regex(pattern);

        foreach (var str in source)
        {
            string result = rgx.Replace(str, replacement);
            Console.WriteLine("Original String:    '{0}'", str);
            Console.WriteLine("Replacement String: '{0}'", result);
        }
        Console.ReadKey();       
    }
}

Output:

Original String:    '# {0}mm {1}mm x {2}mm'
Replacement String: 'foo {0}mm {1}mm x {2}mm'
Original String:    '# {0}mm {1:0.##}mm x {2:0.##}mm'
Replacement String: 'foo {0}mm {1:0.##}mm x {2:0.##}mm'
Original String:    '{0}mm {1}mm x {2}mm # #'
Replacement String: '{0}mm {1}mm x {2}mm foo #'
Original String:    '{0}mm # {1:0.##}mm x {2:0.##}mm'
Replacement String: '{0}mm foo {1:0.##}mm x {2:0.##}mm'
  • -1: This won't work if the target hash is not before the first braced section including another hash (see [this comment](http://stackoverflow.com/questions/24257856/c-sharp-replace-string-if-its-not-within-braces/24258147#comment37472972_24258090)). – Richard Jun 17 '14 at 09:48
  • I have changed the regular expression and it now replaces all # but not between braces. – Sujith Quintelier Jun 19 '14 at 12:45
  • It would be polite to acknowledge that you copied that regex from my answer. – Richard Jun 19 '14 at 16:31