2012-07-10 57 views
1

鑑於EVAL一個公式:其值從列表替換變量名的字符串,並避免重複替換

eval_str = 'VAR1 > 0 and VAR1 < 10 and (VAR2 == VAR1::VALUE_X or VAR2 == VAR2::VALUE_X)' 

任務:我需要更換變量(VAR1,VAR2在這個例子中),與它們的實際值,並用引號圍繞給定的「常量」(VAR1 :: VALUE_X)。

問題:由於變量名稱存在於常量和eval字符串中,並且由於變量可能被包含變量名稱的字符串替換 - 我遇到了常量值中的變量名稱由另一個常量或變量值替換。更好地顯示...

 eval_str = '(VAR2 == VAR2::VALUE_X or VAR2 != VAR2::VALUE_Z) and (VAR1 > 0 and VAR1 < 10)' 
    var_dict = {'VAR1':'5','VAR2':'VAR1::VALUE_Y'} 

    # what I do is strip out most of the special characters 
    # the following findall = ['VAR1', '0', 'and', 'VAR1', '10', 'and', 'VAR2', 'VAR1::VALUE_X', 'or', 'VAR2', 'VAR2::VALUE_X'] 
    for x in re.findall('[^\s()<>=!]+',eval_str): 
     # skip digits, and, or, None *need to improve regex 
     if x.replace('.','').isdigit() or x == 'and' or x == 'or' or x == 'None': 
      continue 
     # if variable is in dict 
     if x in var_dict.keys(): 
      # only replace first 
      eval_str = eval_str.replace(x,var_dict[x],1) 
     # if is constant 
     elif x.__contains__('::'): 
      eval_str = eval_str.replace(x,'\'%s\''%x,1) 

    print eval_str 
    # (5::VALUE_Y == '5::VALUE_Y::VALUE_X' or VAR2 != 'VAR2::VALUE_Z') and (VAR1 > 0 and VAR1 < 10) 

而不是通過每一個變量/值遞增的,也許會有更好的使用正則表達式爲每一個全部更換?或者,如果在每次更換後有記住字符串中的位置的方法,我是否可以修復現有的解決方案?

TYVM!

+0

這應該是解析器的一部分? – 2012-07-10 13:21:01

+0

這些是來自以前框架的驗證字符串,它們假設只是傳遞給eval()調用,但現在我必須注入實際值,因爲變量不再適用。 – breakbadjames 2012-07-10 14:21:01

回答

0

要更換VAR1沿VAR1::VALUE_X讓你可以使用負前瞻:

string = re.sub(r'VAR\d(?!\:\:)', lambda m: var_dict[m.group(0)], string) 

一個更強大的解決方案將字符串解析成AST和eval它。

+0

這有效,但我必須驗證該變量只存在於自身或位於::之前,但它也存在爲:VAR1:實例{1},這會如何影響前瞻? VAR \ d(?!\:)(?!\:\ :) ?? – breakbadjames 2012-07-10 14:30:05

+0

'r'VAR \ d(?!\ :)'' – jfs 2012-07-10 14:37:56

1

看來(我)這樣可以用字符串格式化更容易做到 - 假設你在輸入字符串稍微控制:這個

>>> d={'action':'bar','verb':'foo'} 
>>> print ("don't %(action)s a %(verb)s"%d) 

或者如何:

import re 
class DictFormatter(dict): 
    def __missing__(self,k): 
     return k 

eval_str = '(VAR2 == VAR2::VALUE_X or VAR2 != VAR2::VALUE_Z) and (VAR1 > 0 and VAR1 < 10)' 
var_dict = DictFormatter() 
var_dict.update({'VAR1':'5','VAR2':'VAR1::VALUE_Y'}) 

extra_space = re.compile(r'[\w:]+') #words are alphanumeric + ':' + '_'. 
eval_str = extra_space.sub(lambda m: ' %s '%(m.group()),eval_str) #make sure there is space between "words" 
eval_list = [var_dict[item] for item in eval_str.split()] 

print " ".join(eval_list) 
+0

不幸的是,我對輸入字符串有零控制 – breakbadjames 2012-07-10 13:32:18