Remove spaces within brackets except for text in quotation marks

Viewed 274

I'm looking for a regex which can remove spaces when they are between matching brackets, i.e. ( and ), except when there are spaces inside quotation marks ( ' or ") inside the brackets.

I currently have the regex \s+(?=[^(]*\)), which removes all spaces found between brackets. So also when there are spaces in quotation marks.

// My input
do something with(in = 1, text='some text with spaces' , text2="also has spaces")

// My current output
do something with(in=1,text='sometextwithspaces',text2="alsohasspaces")

// My desired output
do something with(in=1,text='some text with spaces',text2="also has spaces")

Furthermore:

  • Quotation marks can only be found inside the brackets
  • There can be " in ' ' text: text='text with " inside', without the \ escape character.
  • There can be ' in " " text: text="text with ' inside", without the \ escape character.
  • There is no escape character before the quotation mark in the string: text='This is \" not there'

I know there are quite some questions about regex patterns, but I couldn't find one which did the trick. Among the many things that I've tried were look forwards to only find things in between ( until ' or " with \s+(?=[^("]*[\)"]), but that still finds the spaces in between " and ".

Can someone point me in the right direction?

2 Answers

Well, since you have two kind of quotations, " and ' you have to deal with quotation of quotation:

  abc"def pqr' ijk" "klm ' xyz"

Note that both apostrophes are enquoted and that's why don't work. Same with brackets. I doubt if a simple regular expression can help out here, but Finite State Machine can:

private static string RemoveSpaces(string value) {
  if (string.IsNullOrEmpty(value))
    return value;

  bool inQuotation = false;
  bool inApostroph = false;
  int bracketCount = 0;
  int escapeCount = 0;
  StringBuilder result = new StringBuilder(value.Length);

  foreach (char c in value) {
    if (inQuotation) {
      result.Append(c);
      inQuotation = c != '"' || (escapeCount % 2 != 0);
    }
    else if (inApostroph) {
      result.Append(c);
      inApostroph = c != '\'' || (escapeCount % 2 != 0);
    }
    else {
      if (c != ' ' || bracketCount <= 0)
        result.Append(c);

      if (c == '(')
        bracketCount += 1;
      else if (bracketCount == ')')
        bracketCount -= 1;

      inQuotation = c == '"' && (escapeCount % 2 == 0);
      inApostroph = c == '\'' && (escapeCount % 2 == 0);
    }

    escapeCount = c == '\\' ? escapeCount + 1 : 0;
  }
  return result.ToString();
}

Demo:

string test =
  @"do something with(in = 1, text='some text with spaces' , text2=""also has spaces"")";

Console.WriteLine(RemoveSpaces(test));

Outcome:

do something with(in=1,text='some text with spaces',text2="also has spaces")

I made some modifications to the regular expression you used :

# match a space or more 
# if the fol
(?<!['"][^,]*)\s+(?=[^(]*\))|\s+(?![^,]*['"])(?=[^(]*\))

the regular expression is split into two parts the regular expression will match either one of them:

  1. the first part (?<!['"][^,]*)\s+(?=[^(]*\)) matches as much white space as possible \s+ that's not preceded by a quote '" ((?<!['"][^,]*)) and if only it's followed by a closing parenthesis. (?=[^(]*\))
  2. the second part \s+(?![^,]*['"])(?=[^(]*\)) matches as much white space as possible \s+ that's not succeeded by a quote (?![^,]*['"]) and only if it's followed by a closing parenthesis (?=[^(]*\)).

you can test it here

Related