2009-12-30 84 views
4

我需要在JavaScript中編寫一個分割函數,它將字符串分割爲數組,逗號......但逗號不能用引號括起來( '")。只有當分隔符不用引號括起來時才分割字符串

這裏有三個例子,結果(數組)如何應該是:

"peanut, butter, jelly" 
    -> ["peanut", "butter", "jelly"] 

"peanut, 'butter, bread', 'jelly'" 
    -> ["peanut", "butter, bread", "jelly"] 

'peanut, "butter, bread", "jelly"' 
    -> ["peanut", 'butter, bread', "jelly"] 

我不能使用JavaScript的split方法的原因是因爲它也分裂時,分隔符用引號括起來。

如何才能做到這一點,也許正則表達式?


至於背景下,我將使用這個分裂擴大jQuery的$.expr[':']當您從傳遞給函數的第三個參數的第三個要素傳遞的參數。通常,給這個參數的名字叫做meta,它是一個包含關於過濾器的某些信息的數組。

無論如何,這個數組的第三個元素是一個字符串,它包含與過濾器一起傳遞的參數;並且由於字符串格式的參數,我需要能夠正確地分割它們以進行解析。

+0

就可以控制整個集合,以確保所有的元素都包含在單引號內並不會包含本身內的任何單引號? – 2009-12-30 19:16:08

+0

圍繞這個問題的更多背景將會很有趣。它看起來像你試圖從字符串解析JavaScript,或者實際上是JSON。即使在像這樣解析數組的最簡單的情況下,也可能有更好的方法來處理這個問題。 – 2009-12-30 19:23:20

+0

對此,正則表達式是錯誤的工具,正如討論過很多很多次一樣...... – dmckee 2009-12-30 19:27:15

回答

3

你所要求的基本上是一個JavaScript CSV解析器。在「Javascript CSV解析器」上進行Google搜索,您將獲得許多點擊,其中很多都帶有完整的腳本。又見Javascript code to parse CSV data

+0

只是爲了保持平衡,對於這個問題,「詞法分析器」是比「解析器」更合適的術語。 – 2009-12-31 04:51:19

1
var str = 'text, foo, "haha, dude", bar'; 
var fragments = str.match(/[a-z]+|(['"]).*?\1/g); 

更妙的是(支持逃過"'):

var str = 'text_123 space, foo, "text, here\", dude", bar, \'one, two\', blob'; 
var fragments = str.match(/[^"', ][^"',]+[^"', ]|(["'])(?:[^\1\\\\]|\\\\.)*\1/g); 

// Result: 
0: text_123 space 
1: foo 
2: "text, here\", dude" 
3: bar 
4: 'one, two' 
5: blob 
+0

不處理交換的'''和'''定界符,非alpha字符。 – 2009-12-30 19:26:16

+0

改變它,現在它:) – watain 2009-12-30 19:54:54

+0

我運行它與一個簡單的測試,''a,b「'它返回'null'。顯然,它不會接收包含少於3個字符的單詞 – 2009-12-30 21:21:31

-1

如果你能控制輸入執行該字符串將用雙引號括起來",並且所有包含字符串的元素將被括在單引號'中,並且沒有元素可以包含單引號,則可以分割爲, '。如果你無法控制輸入,那麼使用正則表達式對輸入進行排序/過濾/拆分將與使用正則表達式匹配xhtml一樣有用(請參閱:RegEx match open tags except XHTML self-contained tags

+0

沒有看到你鏈接的線程與這個有什麼關係,好吧,在這種情況下我不會使用正則表達式,但是這裏面臨的問題與嘗試使用正則表達式解析(x)html沒有多大關係。這個*由於(x)html的遞歸性質而無法完成,但是這個問題根本就不是這個問題。似乎在每一個帶有「regex」字樣的線程中,線程(#1732348)被張貼一個鏈接... – 2009-12-30 19:57:16

+0

關鍵是,如果你不確定你的輸入可能包含什麼,那麼沒有正則表達式會解析它。 – 2009-12-30 20:09:30

+0

我的觀點是,後#1732348沒有什麼關係這個,而且你不知道你的輸入是什麼,這正是正則表達式的意義:你定義了一個po的模式可能的變化。 – 2009-12-30 20:29:53

1

那麼,我已經有了編寫解決方案的手提鑽(用於其他方面的通用代碼),僅用於踢球。 。 。

function Lexer() { 
    this.setIndex = false; 
    this.useNew = false; 
    for (var i = 0; i < arguments.length; ++i) { 
    var arg = arguments [i]; 
    if (arg === Lexer.USE_NEW) { 
     this.useNew = true; 
    } 
    else if (arg === Lexer.SET_INDEX) { 
     this.setIndex = Lexer.DEFAULT_INDEX; 
    } 
    else if (arg instanceof Lexer.SET_INDEX) { 
     this.setIndex = arg.indexProp; 
    } 
    } 
    this.rules = []; 
    this.errorLexeme = null; 
} 

Lexer.NULL_LEXEME = {}; 

Lexer.ERROR_LEXEME = { 
    toString: function() { 
    return "[object Lexer.ERROR_LEXEME]"; 
    } 
}; 

Lexer.DEFAULT_INDEX = "index"; 

Lexer.USE_NEW = {}; 

Lexer.SET_INDEX = function (indexProp) { 
    if (!(this instanceof arguments.callee)) { 
    return new arguments.callee.apply (this, arguments); 
    } 
    if (indexProp === undefined) { 
    indexProp = Lexer.DEFAULT_INDEX; 
    } 
    this.indexProp = indexProp; 
}; 

(function() { 
    var New = (function() { 
    var fs = []; 
    return function() { 
     var f = fs [arguments.length]; 
     if (f) { 
     return f.apply (this, arguments); 
     } 
     var argStrs = []; 
     for (var i = 0; i < arguments.length; ++i) { 
     argStrs.push ("a[" + i + "]"); 
     } 
     f = new Function ("var a=arguments;return new this(" + argStrs.join() + ");"); 
     if (arguments.length < 100) { 
     fs [arguments.length] = f; 
     } 
     return f.apply (this, arguments); 
    }; 
    })(); 

    var flagMap = [ 
     ["global", "g"] 
    , ["ignoreCase", "i"] 
    , ["multiline", "m"] 
    , ["sticky", "y"] 
    ]; 

    function getFlags (regex) { 
    var flags = ""; 
    for (var i = 0; i < flagMap.length; ++i) { 
     if (regex [flagMap [i] [0]]) { 
     flags += flagMap [i] [1]; 
     } 
    } 
    return flags; 
    } 

    function not (x) { 
    return function (y) { 
     return x !== y; 
    }; 
    } 

    function Rule (regex, lexeme) { 
    if (!regex.global) { 
     var flags = "g" + getFlags (regex); 
     regex = new RegExp (regex.source, flags); 
    } 
    this.regex = regex; 
    this.lexeme = lexeme; 
    } 

    Lexer.prototype = { 
     constructor: Lexer 

    , addRule: function (regex, lexeme) { 
     var rule = new Rule (regex, lexeme); 
     this.rules.push (rule); 
     } 

    , setErrorLexeme: function (lexeme) { 
     this.errorLexeme = lexeme; 
     } 

    , runLexeme: function (lexeme, exec) { 
     if (typeof lexeme !== "function") { 
      return lexeme; 
     } 
     var args = exec.concat (exec.index, exec.input); 
     if (this.useNew) { 
      return New.apply (lexeme, args); 
     } 
     return lexeme.apply (null, args); 
     } 

    , lex: function (str) { 
     var index = 0; 
     var lexemes = []; 
     if (this.setIndex) { 
      lexemes.push = function() { 
      for (var i = 0; i < arguments.length; ++i) { 
       if (arguments [i]) { 
       arguments [i] [this.setIndex] = index; 
       } 
      } 
      return Array.prototype.push.apply (this, arguments); 
      }; 
     } 
     while (index < str.length) { 
      var bestExec = null; 
      var bestRule = null; 
      for (var i = 0; i < this.rules.length; ++i) { 
      var rule = this.rules [i]; 
      rule.regex.lastIndex = index; 
      var exec = rule.regex.exec (str); 
      if (exec) { 
       var doUpdate = !bestExec 
       || (exec.index < bestExec.index) 
       || (exec.index === bestExec.index && exec [0].length > bestExec [0].length) 
       ; 
       if (doUpdate) { 
       bestExec = exec; 
       bestRule = rule; 
       } 
      } 
      } 
      if (!bestExec) { 
      if (this.errorLexeme) { 
       lexemes.push (this.errorLexeme); 
       return lexemes.filter (not (Lexer.NULL_LEXEME)); 
      } 
      ++index; 
      } 
      else { 
      if (this.errorLexeme && index !== bestExec.index) { 
       lexemes.push (this.errorLexeme); 
      } 
      var lexeme = this.runLexeme (bestRule.lexeme, bestExec); 
      lexemes.push (lexeme); 
      } 
      index = bestRule.regex.lastIndex; 
     } 
     return lexemes.filter (not (Lexer.NULL_LEXEME)); 
     } 
    }; 
})(); 

if (!Array.prototype.filter) { 
    Array.prototype.filter = function (fun) { 
    var len = this.length >>> 0; 
    var res = []; 
    var thisp = arguments [1]; 
    for (var i = 0; i < len; ++i) { 
     if (i in this) { 
     var val = this [i]; 
     if (fun.call (thisp, val, i, this)) { 
      res.push (val); 
     } 
     } 
    } 
    return res; 
    }; 
} 

現在使用的代碼爲您的問題:

function trim (str) { 
    str = str.replace (/^\s+/, ""); 
    str = str.replace (/\s+$/, ""); 
    return str; 
} 

var splitter = new Lexer(); 
splitter.setErrorLexeme (Lexer.ERROR_LEXEME); 
splitter.addRule (/[^,"]*"[^"]*"[^,"]*/g, trim); 
splitter.addRule (/[^,']*'[^']*'[^,']*/g, trim); 
splitter.addRule (/[^,"']+/g, trim); 
splitter.addRule (/,/g, Lexer.NULL_LEXEME); 

var strs = [ 
    "peanut, butter, jelly" 
    , "peanut, 'butter, bread', 'jelly'" 
    , 'peanut, "butter, bread", "jelly"' 
    ]; 

// NOTE: I'm lazy here, so I'm using Array.prototype.map, 
//  which isn't supported in all browsers. 
var splitStrs = strs.map (function (str) { 
    return splitter.lex (str); 
}); 
相關問題