2014-03-01 45 views
1

作爲對我的問題Tokenizing an infix string in Java的回答,我得到了正則表達式(?<=[^\.a-zA-Z\d])|(?=[^\.a-zA-Z\d]。但是,現在我正在使用Javascript編寫相同的代碼,並且我堅持要如何使用Javascript正則表達式來執行相同的操作。將Java標記正則表達式轉換爲Javascript

舉例來說,如果我有串sin(4+3)*2,我需要它解析爲["sin","(","4","+","3",")","*","2"]

我會用什麼正則表達式的字符串標記化到每個單獨的一部分。

之前,我做的是我只是做了一個字符串替換每個可能的標記,並在其周圍放置一個空格,然後拆分該空白。但是,該代碼很快變得非常臃腫。

我需要拆分的將是標準的數學運算符(+,-,*,/,^),以及函數名(sin,cos,tan,abs,etc...)的運營商和逗號

什麼是快速,有效的方式做到這一點?

回答

2

您可以利用正則表達式分組來執行此操作。您需要一個結合了不同可能令牌的正則表達式,然後重複應用它。

我喜歡分離出不同的部分;它可以更容易維護和擴展:

var tokens = [ 
    "sin", 
    "cos", 
    "tan", 
    "\\(", 
    "\\)", 
    "\\+", 
    "-", 
    "\\*", 
    "/", 
    "\\d+(?:\\.\\d*)?" 
]; 

你粘上這些都連成一個大的正則表達式與|每個標記之間:

var rtok = new RegExp("\\s*(?:(" + tokens.join(")|(") + "))\\s*", "g"); 

然後,您可以標記化使用您的源字符串的正則表達式操作:

function tokenize(expression) { 
    var toks = [], p; 

    rtok.lastIndex = p = 0; // reset the regex 
    while (rtok.lastIndex < expression.length) { 
    var match = rtok.exec(expression); 

    // Make sure we found a token, and that we found 
    // one without skipping garbage 

    if (!match || rtok.lastIndex - match[0].length !== p) 
     throw "Oops - syntax error"; 

    // Figure out which token we matched by finding the non-null group 
    for (var i = 1; i < match.length; ++i) { 
     if (match[i]) { 
     toks.push({ 
      type: i, 
      txt: match[i] 
     }); 
     // remember the new position in the string 
     p = rtok.lastIndex; 
     break; 
     } 
    } 
    } 
    return toks; 
} 

這只是重複匹配字符串的令牌正則表達式。正則表達式是用「g」標誌創建的,所以正則表達式機器會在每次匹配後自動跟蹤從哪裏開始匹配。當它看不到匹配,或者它沒有看到匹配時,但必須跳過無效內容才能找到它,我們知道有一個語法錯誤。當它匹配時,它在令牌數組中記錄它匹配的令牌(非空組的索引)和匹配的文本。通過記住匹配的標記索引,它可以節省您在標記後得出每個標記字符串意味着的麻煩;你只需要做一個簡單的數字比較。

因此主叫tokenize("sin(4+3) * cos(25/3)")回報:

[ { type: 1, txt: 'sin' }, 
    { type: 4, txt: '(' }, 
    { type: 10, txt: '4' }, 
    { type: 6, txt: '+' }, 
    { type: 10, txt: '3' }, 
    { type: 5, txt: ')' }, 
    { type: 8, txt: '*' }, 
    { type: 2, txt: 'cos' }, 
    { type: 4, txt: '(' }, 
    { type: 10, txt: '25' }, 
    { type: 9, txt: '/' }, 
    { type: 10, txt: '3' }, 
    { type: 5, txt: ')' } ] 

令牌類型1是sin功能,類型4是左括號,類型10是一個數字,等

編輯,如果你想匹配如「x」和「y」標識 —,那麼我可能會使用一組不同的標記圖案,其中一個正好匹配任何標識。這意味着解析器不會直接從詞法分析器中找出關於「罪」和「cos」等的信息,但沒關係。下面是令牌模式的備選列表:

var tokens = [ 
    "[A-Za-z_][A-Za-z_\d]*", 
    "\\(", 
    "\\)", 
    "\\+", 
    "-", 
    "\\*", 
    "/", 
    "\\d+(?:\\.\\d*)?" 
]; 

現在任何標識符都將是類型1標記。

+0

對不起,但是當我在http://jsfiddle.net/P5vmY/上運行它時,它只是一直拋出一個語法錯誤。我執行錯了嗎? – scrblnrd3

+0

@ scrblnrd3我沒有包含任何符合「x」的內容。它必須是一組稍微不同的令牌模式;而不是明確地查找「罪惡」等,你只需要尋找標識符,並在解析器中單獨定義這些標識符。 – Pointy

+0

@ scrblnrd3哦也有一個錯誤:)我會編輯它。 – Pointy

1

剛提供了幾種可能性:

[a-zA-Z]+|\d+(?:\.\d+)?|. 
2

我不知道這是否會做你想要達到的目標一切,但它爲我工作:

'sin(4+3)*2'.match(/\d+\.?\d*|[a-zA-Z]+|\S/g); 

// ["sin", "(", "4", "+", "3", ")", "*", "2"] 

您可以用替代[a-zA-Z]+部分,以僅支持數學函數。

相關問題