2017-05-26 325 views
1

我正在尋找從數學方程字符串中刪除數學上不需要的括號。我需要這樣做,最好是在PostgreSQL 6或VBA中。VBA或PostgreSQL:從數學方程字符串中刪除不需要的括號

例如,我有一個PostgreSQL數據庫以下字符串值:

PercentileRank((([bp47244]+([bp47229][ttm]))/(AvgAeTe([bp48918])))) 

,我需要它看起來像這樣(編輯/修正):

PercentileRank(([bp47244]+[bp47229][ttm])/AvgAeTe([bp48918])) 

我寧願PostgreSQL中的函數或查詢,但VBA解決方案可以工作。

注意PercentileRank()AvgAeTe()是函數。這[bp47244][bp47229][ttm]每個代表單個數字/變量,但它們可以以任何方式表示,如[abc123][xyz321][ttm]。我在那裏看到很多例子,但我沒有看到使用PostgreSQL或VBA的版本,所以我認爲這將是一個很好的問題。

當然,我正在尋找一個可應用於任何方程的通用解決方案。

我現在正在處理這個問題,所以如果我在找到一個答案之前發佈,我會分享;然而,我不擅長正則表達式(不是解決方案必須使用正則表達式)。

謝謝!

更新: 我工作過這樣的邏輯:從這個鏈接

Let L be operator immediately left of the left parenthesis, or nil 
Let R be operator immediately right of the right parenthesis, or nil 
If L is nil and R is nil: 
    Redundant 
Else: 
    Scan the unparenthesized operators between the parentheses 
    Let X be the lowest priority operator 
    If X has lower priority than L or R: 
    Not redundant 
    Else: 
    Redundant 

Remove redundant parentheses from an arithmetic expression

我將代碼VBA遵循這一邏輯的東西了,並張貼一個答案。

+1

這太辛苦了正則表達式。你需要解析表達式。 –

+1

...另外,我不確定你的例子是否正確。 '/'具有(通常)比'+更高的優先級,所以在本例中,您也刪除了「需要」的括號。 – pozs

+0

@pozs - 對不起。我糾正了它。謝謝!另外,它不一定是正則表達式。 – mountainclimber

回答

0

這似乎爲我的情況下工作:

Function RemoveParens(s As String) As String 
'remove unecessary parentheses 
'exponents not implemented 
'mathematical brackets are not implmented (it is assumed that only parentheses are used to create mathematical order) 
    'brakets are assumed to identify a variable or calculation on a variable 
     '[bp47229][ttm] -> one value/variable; [xyz123] -> one value/variable 
'logic based on Antti Huima's answer: 
    'https://stackoverflow.com/questions/44203517/vba-or-postgresql-remove-unneeded-parentheses-from-a-mathematical-equation-stri 


's = "PercentileRank((([bp47244]+([bp47229][ttm]))/(AvgAeTe([bp48918]))))" 
's = "PercentileRank(2*(1+3)(5*4))" 
If InStr(1, s, "^") > 0 Then 
    msgbox "Exponents are not implemented in RemoveParens" 
End If 

ReDim arS(1 To Len(s)) As String 
Dim i As Integer 
For i = 1 To Len(s) 
    arS(i) = Mid(s, i, 1) 
Next i 
Dim iCnt As Integer 
iCnt = 0 
Dim iLen As Integer 
iLen = Len(s) 
Dim sTmp As String 
Dim bRemove As Boolean 
bRemove = False 
Dim sLfOpr As String 
Dim sRtOpr As String 
Dim iCntBtwn As Integer 
Dim sLast As String 
'loop through chars 
Do 
    iCnt = iCnt + 1 
    sTmp = Mid(s, iCnt, 1) 

    If sTmp = "(" Then 
     if iCnt - 1 <= 0 then 
      sLfOpr = "" 
     else 
      sLfOpr = Mid(s, iCnt - 1, 1) 
     end if 
     'in case we have "5(...) or (...)(...) 
     If IsNumeric(sLfOpr) Or sLfOpr = ")" Then 
      sLfOpr = "*" 
     End If 

     'if it isn't an oper then clear it 
     If sLfOpr <> "+" _ 
      And sLfOpr <> "-" _ 
      And sLfOpr <> "/" _ 
      And ((Not IsAlpha(sLfOpr) = True) Or (Not Mid(s, iCnt, 1) = "(")) _ 
      And sLfOpr <> "*" _ 
      Then 
      sLfOpr = "" 
     End If 

     'find the matching paren to the right of LfOpr 
     Dim iCntR As Integer 
     iCntR = iCnt 

     Dim iCntParen As Integer 
     iCntParen = 1 
     Dim sTmpR As String 
     sTmpR = "" 
     Do 
      iCntR = iCntR + 1 
      sTmpR = Mid(s, iCntR, 1) 

      If sTmpR = "(" Then 
       iCntParen = iCntParen + 1 
      ElseIf sTmpR = ")" Then 
        iCntParen = iCntParen - 1 
      End If 

      'we found the close paren that matches the open paren 
      If iCntParen = 0 Then 
       sRtOpr = Mid(s, iCntR + 1, 1) 
       'in case we have "(...)5 or (...)(...) 
       If IsNumeric(sRtOpr) Or sRtOpr = "(" Then 
        sRtOpr = "*" 
       End If 
       If sRtOpr <> "+" _ 
        And sRtOpr <> "-" _ 
        And sRtOpr <> "/" _ 
        And ((Not IsAlpha(sRtOpr) = True) Or (Not Mid(s, iCntR, 1) = "(")) _ 
        And sRtOpr <> "*" _ 
        Then 
        sRtOpr = "" 
       End If 
       If sRtOpr = "" And sLfOpr = "" Then 
        arS(iCnt) = "" 
        arS(iCntR) = "" 
        'go to the next overall open paren 
        Exit Do 
       Else 
        ' ------------ search btwn parens ------------------- 
        Dim iCntParenOp As Integer 
        Dim iCntParenCl As Integer 
        iCntParenOp = 0 
        iCntParenCl = 0 
        Dim sTmpB As String 
        sTmpB = "" 
        Dim sLowOpr As String 
        sLowOpr = "" 
        Dim iCntRLw As Integer 
        iCntRLw = iCnt 
        Dim bInSub As Boolean 
        bInSub = False 
        Dim bNoOpr As Boolean 
        bNoOpr = True 
        'loop through chars between the two parens 
        For i = iCnt + 1 To iCntR 
         iCntRLw = iCntRLw + 1 
         sTmpR = Mid(s, iCntRLw, 1) 
         If sTmpR = "(" Then 
          iCntParenOp = iCntParenOp + 1 
          bInSub = True 
         ElseIf sTmpR = ")" Then 
           iCntParenCl = iCntParenCl + 1 
           If bInSub = True And iCntParenCl = iCntParenOp Then 
            bInSub = False 
           End If 
         End If 
         'we found the close paren that matches the open paren 
          'and we are not in a nested/sub paren 
         If bInSub = False Then 
          'in case we have "(...)5 or (...)(...) 
          If (IsNumeric(sTmpR) And Mid(s, iCntRLw + 1, 1) = "(") Or (sTmpR = "(" And Mid(s, iCntRLw + 1, 1) = "(") Then 
           sTmp = "*" 
          End If 
          'it is an operator 
          If sTmpR = "+" _ 
           Or sTmpR = "-" _ 
           Or sTmpR = "/" _ 
           Or ((IsAlpha(sTmpR) = True) And (Mid(s, iCntRLw + 1, 1) = "(")) _ 
           Or sTmpR = "*" _ 
           Or bNoOpr = True _ 
           Then 

            'see if sLowROpr operater has lower priority than sLfOpr and sRtOpr 
            If Not IsLowerPri(sTmpR, sRtOpr, sLfOpr) Then 
              arS(iCnt) = "" 
              arS(iCntR) = "" 
              Exit For 
            End If 
            bNoOpr = False 
          End If 

         End If 
        Next i 
       End If 
       Exit Do 'always stop loop if iCntParen = 0 
      End If 

     Loop While iCntR <> iLen 

    End If 
Loop While iCnt <> iLen 

Dim sOut As String 
For i = LBound(arS) To UBound(arS) 
    sOut = sOut & arS(i) 
Next i 
'Debug.Print s 
RemoveParens = sOut 
End Function 
Function IsLowerPri(sTestOpr As String, sRtOpr As String, sLfOpr As String) As Boolean 
'exponents not implemented yet 
Dim iTestOpr As Integer 
Dim iRtOpr As Integer 
Dim iLfOpr As Integer 
iTestOpr = 1 
If sTestOpr = "+" Or sTestOpr = "-" Then 
    iTestOpr = 1 
ElseIf sTestOpr = "*" Or sTestOpr = "/" Then 
    iTestOpr = 2 
ElseIf IsAlpha(sTestOpr) And sTestOpr <> "" Then 
    iTestOpr = 3 
End If 

If sRtOpr = "+" Or sRtOpr = "-" Then 
    iRtOpr = 1 
ElseIf sRtOpr = "*" Or sRtOpr = "/" Then 
    iRtOpr = 2 
ElseIf IsAlpha(sRtOpr) And sRtOpr <> "" Then 
    iRtOpr = 3 
End If 

If sLfOpr = "+" Or sLfOpr = "-" Then 
    iLfOpr = 1 
ElseIf sLfOpr = "*" Or sLfOpr = "/" Then 
    iLfOpr = 2 
ElseIf IsAlpha(sLfOpr) And sLfOpr <> "" Then 
    iLfOpr = 3 
End If 

If iTestOpr < iRtOpr Or iTestOpr < iLfOpr Then 
    IsLowerPri = True 
Else 
    IsLowerPri = False 
End If 
End Function 

它需要大量的清理,可能有一些測試。我會給予答覆,不管是最好的改進還是不同的解決方案,都會更好。

UPDATE: 忘記這個功能:

Public Function IsAlpha(strValue As String) As Boolean 
    IsAlpha = strValue Like WorksheetFunction.Rept("[a-zA-Z]", Len(strValue)) 
End Function 
相關問題