2010-05-16 36 views
2

有沒有辦法在JavaScript中解析字符串中的數學表達式?例如,假設我想產生字符串「湯姆有2個蘋果,露西有3個蘋果,他們一起有5個蘋果」,但我希望能夠替代變量。我可以用一個字符串替換:用字符串替換計算

string = "Tom has X apples, Lucy has Y apples. Together they have Z apples"; 
string2 = string.replace(/X/, '2').replace(/Y/, '3').replace(/Z/, '5'); 

但是,如果不使用變量Z,我可以使用X + Y來代替它。現在,我還可以爲X + Y做一個字符串替換,並將其替換爲正確的值,但是當試圖處理所有可能的字符串計算時,這可能會變得雜亂無章。我想我正在尋找一種方式來實現這一目標:

string = "Something [X], something [Y]. Something [(X+Y^2)/(5*X)]"; 

而對於[___]部分應被理解爲表達式替換回字符串之前得到解決。

感謝您的幫助。

+0

你是一個失敗者與「權力」運營商。在JavaScript中,這意味着異或。 – Eric 2010-05-16 11:50:15

+0

@Eric:這是一個很好的觀點。 – 2010-05-16 12:35:36

回答

4

有沒有直接的,內置的方式(好吧,好吧,也許有  —見下文),但是如果你使用replace功能,其中置換可以是功能而不是字符串的回撥功能(返回值是被替換的),你可以很容易地實現它。

例如,假設您爲佔位符使用Ruby符號#{xyz}。代碼遍歷這些:

var mappings, str; 

str = "One #{X} three #{Y} five"; 
mappings = { 
    "X": 2, 
    "Y": 4 
}; 
str = str.replace(/\#\{([^#]+)\}/g, function(match, key) { 
    var result; 

    result = mappings[key]; 
    /* ...processing here */ 
    return result; 
}); 

結果字符串是One 2 three 4 five,因爲#{X}#{Y}已經通過查找替換。你可以看看這個鍵,看看它是否是一個表達式,並且需要被評估而不是簡單地查找。這項評估是你真正的工作進來

現在,你可以使用witheval實現表達式的支持。改變result = mapping[key];線以上這樣的:

with (mappings) { 
     result = eval(key); 
    } 

如果你喂字符串"One #{X} three #{Y} five #{X + Y * 2}"成,結果是One 2 three 4 five 10   —因爲2 + 4 * 2 = 10

做是因爲with枝之上給定對象範圍鏈,所以它是第一件事時檢查解決一個不合格的參考(如X),並且eval執行Javascript代碼  —所以可以評估表達式  —,並在所謂的範圍內神奇地這樣做。但要小心;正如Eric所指出的那樣,並非所有運營商在各種表達形式上都是相同的,特別是Javascript將^解釋爲「按位異或」,而不是「對於權力」。 (它沒有一個指數運算符,你必須使用Math.pow。)

但是你必須非常小心之類的事情,無論是witheval(各以自己的方式)是有問題的。但是with的主要問題在於,如果你做了一個任務,很難說出事情的來源或發生的方向,而你卻不知道;並且eval的主要問題來自於使用它來解釋不受控制的字符串。只要你保持保障機制,並意識到問題...

沸騰下來到一個函數:

function evaluate(str, mappings) { 
    return str.replace(/\#\{([^#]+)\}/g, function(match, key) { 
     var result; 

     with (mappings) { 
      result = eval(key); 
     } 

     return result; 
    }); 
} 
alert(evaluate(
    "The expression '(#{X} + #{Y}) * 2' equals '#{(X + Y) * 2}'", 
    {"X": 2, "Y": 4} 
)); // alerts "The expression '(2 + 4) * 2' equals '12'" 
alert(evaluate(
    "The expression '(#{X} + #{Y}) * 2' equals '#{(X + Y) * 2}'", 
    {"X": 6, "Y": 3} 
)); // alerts "The expression '(6 + 3) * 2' equals '18'" 
+1

這非常好 - 比我想象的要短得多。 +1 – Kobi 2010-05-16 11:46:46

+0

不錯的W3Schools不會記錄'替換'的特殊功能... http://www.w3schools.com/jsref/jsref_replace.asp – Eric 2010-05-16 11:49:08

+0

@Eric:是的,w3schools很方便,但不可靠,如果你跟着我。 MDC(https://developer.mozilla.org/En)通常很不錯(他們目前遇到系統問題)。我通常只搜索函數名稱加上「mdc」或「msdn」(取決於我是否需要檢查IE怪癖)。 – 2010-05-16 12:34:06

1

我能想到的唯一方法就是實現這個功能,它是一個模板引擎,如jTemplates。也請參閱this SO問題的答案。

1

有趣的問題:

function substitutestring(str,vals) 
{ 
    var regex = /\[[^\]]*\]/gi; 
    var matches = str.match(regex); 
    var processed = []; 

    for(var i = 0; i<matches.length; i++) 
    { 
     var match = matches[i]; 
     processed[match] = match.slice(1,-1); 

     for(j in vals) 
     { 
      processed[match] = processed[match].replace(j,vals[j]); 
     } 

     processed[match] = eval("("+processed[match]+")"); 
    } 

    for(var original in processed) 
    { 
     str = str.replace(original,processed[original]); 
    } 
    return str; 
} 

document.write(
    substitutestring(
     "[x] + [y] = [x+y]", 
     {"x": 1, "y": 2} 
    ) 
); 
0

在ES6您現在可以使用template strings

var X = 2,Y = 3; string = Tom has ${X} apples, Lucy has ${Y} apples. Together they have ${X+Y} apples;