0

I have a service that formats strings in certain fields. Basically, when a user clicks out of the input box (on blur), the string is cleansed of illegal characters, and whitespace is replaced with commas. This is fine, but I would like to allow a user to add double quotes around grouped words. On blur, this should remove the quotes, but maintain the space in between the words, and then add a comma afterwards. I have tried everything but I can't get this to work. Here is how my service is currently set up:

angular.module('testApp')
  .factory('formatStringService', [
    function () {
      return {
        formatString: function (string) {
          var styleStr = string;

          if (styleStr === undefined) {
            return;
          }
          styleStr = this.stringReplace(styleStr, '\n', ',');
          styleStr = this.stringReplace(styleStr, '\t', ',');
          styleStr = this.stringReplace(styleStr, ' ', ',');
          styleStr = this.stringReplace(styleStr, ';', ',');
          styleStr = this.newLine(styleStr);
          styleStr = this.validated(styleStr);

          for (var g = 0; g < 9; g++) {
            styleStr = this.stringReplace(styleStr, ',,', ',');
          }
          if (styleStr.charAt(styleStr.length - 1) === ',') {
            styleStr = styleStr.substr(0, (styleStr.length - 1));
          }
          if (styleStr.charAt(0) === '*') {
            styleStr = styleStr.substr(1, (styleStr.length - 1));
          }
          if (styleStr.charAt(styleStr.length - 1) === '*') {
            styleStr = styleStr.substr(0, (styleStr.length - 1));
          }
          return styleStr;
        },

        stringReplace: function (string, text, by) {
          var strLength = string.length,
            txtLength = text.length;
          if ((strLength === 0) || (txtLength === 0)) {
            return string;
          }
          var i = string.indexOf(text);
          if ((!i) && (text !== string.substring(0, txtLength))) {
            return string;
          }
          if (i === -1) {
            return string;
          }
          var newstr = string.substring(0, i) + by;
          if (i + txtLength < strLength) {
            newstr += this.stringReplace(string.substring(i + txtLength, strLength), text, by);
          }
          return newstr;
        },
        validated: function (string) {
          for (var i = 0, output = '', valid = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,~#+/\\*- '; i < string.length; i++) {
            if (valid.indexOf(string.charAt(i)) !== -1) {
              output += string.charAt(i);
            }
          }
          return output;
        },

        newLine: function (string) {
          for (var i = 0, output = ''; i < string.length; i++) {
            if (string.charCodeAt(i) !== 10) {
              output += string.charAt(i);
            }
          }
          return output;
        }
      };
    }
  ]);

Input string example: 1 2 3 "test test" 7 8

Should output: 1,2,3,test test,7,8

developthewebz
  • 1,827
  • 3
  • 17
  • 43
  • 1
    Use [`'1 2 3 "test test" 7 8'.replace(/\s+(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)/g, ',').replace(/"/g, '');`](https://regex101.com/r/rQ0jD6/1) [**Fiddle**](https://jsfiddle.net/tusharj/y4mmLdv3/) – Tushar Dec 22 '15 at 14:53

2 Answers2

1

Here's a neat regex trick that you can use for this purpose:

var indices = [],
    re = /"[^"]*"|( )/g,
    str = '1 2 3 "test test" 7 8';

while ((match = re.exec(str)) !== null) {
    if (match[1] !== undefined) indices.push(match.index);
}

var split = [], prevIndex = -1;
indices.forEach(function(index) {
    split.push(str.slice(prevIndex + 1, index));
    prevIndex = index;
});

document.getElementById('output').innerText = split.join('\n');
<pre id='output'></pre>

What we're doing here is matching on the regex /"[^"]*"|( )/—that is, either "stuff between quotes" or "a single space." So if we find a quote, we immediately start matching "stuff between quotes" (because regex is greedy), and hence any spaces between quotes are just gobbled up in that section of the regex.

Then we know that the ( ) will only be matched if we're not inside double quotes. So we stick the space into a capture group, and then for every match we can simply check whether the capture group exists.

tckmn
  • 57,719
  • 27
  • 114
  • 156
1

Using a positive look ahead, something like this should do it

'1 2 3 "test test" 7 8'.match(/(".*?"|[^"\s]+)(?=\s|$)/g)

Reg Exp Visualizer

epascarello
  • 204,599
  • 20
  • 195
  • 236