How to remove any () and their contents from a string variable in Excel VBA without regex

huangapple go评论104阅读模式
英文:

How to remove any () and their contents from a string variable in Excel VBA without regex

问题

我有一个预定义的函数,它在数据库中查找字符串,并返回一个对象。我需要从工作表中提取一个字符串,删除括号内的内容(以及括号本身),然后将其传递给这个函数。然而,我无法让替换函数找到括号之外的内容。

我尝试了以下代码:

Set feederType = Cells(i, 5)
feederCode = Replace(feederType, "()", "")
feederID = getFeeder(feederCode).ID

其中i是一个循环变量,feederType是一个范围,getFeeder()接受一个字符串并返回一个带有ID属性的对象。我还尝试过将feederType作为字符串来使用,但结果相同。

这只对直接替换"()"有效,并且实际上不会替换括号内的任何内容。有没有办法在字符串变量中进行这种替换,而不涉及正则表达式?

编辑:我已经找到了适用于我的特定用例的解决方案,使用InStr()和Left(),因为括号只出现在我的输入的末尾。然而,我仍然很好奇,是否已经存在用于字符串变量的通配符替换函数。

英文:

I have a pre-defined function that looks up a string in a database, and returns an object. I need to pull a string from a worksheet, remove anything in parentheses(as well as the parentheses), and then pass it to this function. However, I can't get the Replace function to find more than just the parentheses themselves.

I tried the code:

  Set feederType = Cells(i, 5)
  feederCode = Replace(feederType, "(*)", "")
  feederID = getFeeder(feederCode).ID

Where i is a looping variable, feederType is a range, and getFeeder() takes a string and returns an object with the ID property. I have also tried this with feederType as a string, to the same result.

This only works for a direct substitution of "(*)", and does not actually replace anything in parentheses. Is there a way to do this replacement in a string variable without getting into regex?

EDIT: I have found a solution that works for my specific use case using InStr() and Left() since parentheses only appear at the end of my inputs. I am still curious, however, if any wildcard substitution functions already exist for string variables.

答案1

得分: 2

假设只有一组括号,我们可以找到开括号和闭括号的位置,获取子字符串,然后从原始字符串中删除它。对于更复杂的模式,我会使用正则表达式。

Function RemoveParentheses(ByVal Text As String) As String
    Dim Result As String
    Dim OpeningParenPos As Long
    OpeningParenPos = InStr(Text, "(")
    
    Dim ClosingParenPos As Long
    ClosingParenPos = InStr(Text, ")")
    If OpeningParenPos > 0 And ClosingParenPos > 0 Then
        Result = Replace(Text, Mid(Text, OpeningParenPos, ClosingParenPos - OpeningParenPos + 1), "")
    Else
        Result = Text
    End If
    Result = Trim(Result)
    RemoveParentheses = Result
End Function
英文:

Assuming that there is only one set of parenthesis we can find the position of the opening and closing parentheses, get the substring, and remove it from the original string. I would use RegEx for more complex patterns.

Function RemoveParentheses(ByVal Text As String) As String
    Dim Result As String
    Dim OpeningParenPos As Long
    OpeningParenPos = InStr(Text, "(")
    
    Dim ClosingParenPos As Long
    ClosingParenPos = InStr(Text, ")")
    If OpeningParenPos > 0 And ClosingParenPos > 0 Then
        Result = Replace(Text, Mid(Text, OpeningParenPos, ClosingParenPos - OpeningParenPos + 1), "")
    Else
        Result = Text
    End If
    Result = Trim(Result)
    RemoveParentheses = Result
End Function

答案2

得分: 2

以下是您要翻译的内容:

"While I see no point in explicitly not using regular expressions, it can of course be done, but not with a one-liner (at least I cannot think of any).

The easy case is that you have only one pair of parentheses. Search for the leftmost opening and the rightmost closing parentheses.

Function removeParenthesesVersion1(s As String) As String
Dim p1 As Long, p2 As Long
p1 = InStr(s, "(")
p2 = InStrRev(s, ")")
If p1 > 0 And p2 > 0 Then
removeParenthesesVersion1 = Left(s, p1 - 1) & Mid(s, p2 + 1)
Else
removeParenthesesVersion1 = s
End If
End Function

However, if you have more than one pair of parentheses, you need to be a little bit more sophisticated. The following function loops over the string until no more pairs of parentheses can be found. In opposite to the first function, it looks for the leftmost closing parentheses.

Function removeParenthesesVersion2(ByVal s As String) As String
Dim p1 As Long, p2 As Long
Do While True
p1 = InStr(s, "(")
p2 = InStr(s, ")")
If p1 > 0 And p2 > 0 and p2 > p1 Then
removeParenthesesVersion2 = removeParenthesesVersion2 & Left(s, p1 - 1)
s = Mid(s, p2 + 1)
Else
Exit Do
End If
Loop
removeParenthesesVersion2 = removeParenthesesVersion2 & s
End Function

To test this:

Sub test()
Const s1 = "Lorem(xyz)Ipsum"
Const s2 = "Lorem(xyz)Ipsum(123)Dolor Sit"

Debug.Print removeParenthesesVersion1(s1)
Debug.Print removeParenthesesVersion1(s2)
Debug.Print removeParenthesesVersion2(s1)
Debug.Print removeParenthesesVersion2(s2)
End Sub

results in

LoremIpsum
LoremDolor Sit
LoremIpsum
LoremIpsumDolor Sit

It gets even more tricky if you have nested loops and the like, but for this you first need to define what exactly you need as output before starting to code."

英文:

While I see no point in explicitly not using regular expressions, it can of course be done, but not with a one-liner (at least I cannot think of any).

The easy case is that you have only one pair of parentheses. Search for the leftmost opening and the rightmost closing parentheses.

Function removeParenthesesVersion1(s As String) As String
    Dim p1 As Long, p2 As Long
    p1 = InStr(s, "(")
    p2 = InStrRev(s, ")")
    If p1 > 0 And p2 > 0 Then
        removeParenthesesVersion1 = Left(s, p1 - 1) & Mid(s, p2 + 1)
    Else
        removeParenthesesVersion1 = s
    End If
End Function

However, if you have more than one pair of parentheses, you need to be a little bit more sophisticated. The following function loops over the string until no more pairs of parentheses can be found. In opposite to the first function, it looks for the leftmost closing parentheses.

Function removeParenthesesVersion2(ByVal s As String) As String
    Dim p1 As Long, p2 As Long
    Do While True
        p1 = InStr(s, "(")
        p2 = InStr(s, ")")
        If p1 > 0 And p2 > 0 and p2 > p1 Then
            removeParenthesesVersion2 = removeParenthesesVersion2 & Left(s, p1 - 1)
            s = Mid(s, p2 + 1)
        Else
            Exit Do
        End If
    Loop
    removeParenthesesVersion2 = removeParenthesesVersion2 & s
End Function

To test this:

Sub test()
    Const s1 = "Lorem(xyz)Ipsum"
    Const s2 = "Lorem(xyz)Ipsum(123)Dolor Sit"
    
    Debug.Print removeParenthesesVersion1(s1)
    Debug.Print removeParenthesesVersion1(s2)
    Debug.Print removeParenthesesVersion2(s1)
    Debug.Print removeParenthesesVersion2(s2)        
End Sub

results in

LoremIpsum
LoremDolor Sit
LoremIpsum
LoremIpsumDolor Sit

It gets even more tricky if you have nested loops and the like, but for this you first need to define what exactly you need as output before starting to code.

答案3

得分: 1

' 更快一些:
' 从字符串中移除括号内的内容

Function RemoveParenthContent(s As String) As String
    Dim openPar As Long, closePar As Long
    Const Lpar = "(", Rpar = ")"
    openPar = InStr(1, s, Lpar)
    If openPar > 0 Then
        closePar = InStr(openPar, s, Rpar)
        If closePar > openPar Then
            RemoveParenthContent = Left$(s, openPar - 1) & Mid$(s, closePar + 1)
            Exit Function
        End If
    End If
    RemoveParenthContent = s
End Function

' 对于任何一组()
Function RemoveParenthContent(s As String) As String
    Dim openPar As Long, closePar As Long
    Const Lpar = "(", Rpar = ")"
    Do
        openPar = InStr(1, s, Lpar)
        If openPar > 0 Then
            closePar = InStr(openPar, s, Rpar)
            If closePar > openPar Then
                s = Left$(s, openPar - 1) & Mid$(s, closePar + 1)
            Else
                Exit Do
            End If
        Else
            Exit Do
        End If
    Loop
    RemoveParenthContent = s
End Function
英文:

A little faster:

Function RemoveParenthContent(s As String) As String
   Dim openPar As Long, closePar As Long
   Const Lpar = "(", Rpar = ")"
   openPar = InStr(1, s, Lpar)
   If openPar > 0 Then
      closePar = InStr(openPar, s, Rpar)
      If closePar > openPar Then
         RemoveParenthContent = Left$(s, openPar - 1) & Mid$(s, closePar + 1)
         Exit Function
      End If
   End If
   RemoveParenthContent = s
End Function

'for any group of ()
Function RemoveParenthContent(s As String) As String
   Dim openPar As Long, closePar As Long
   Const Lpar = "(", Rpar = ")"
   Do
      openPar = InStr(1, s, Lpar)
      If openPar > 0 Then
         closePar = InStr(openPar, s, Rpar)
         If closePar > openPar Then
            s = Left$(s, openPar - 1) & Mid$(s, closePar + 1)
         Else
            Exit Do
         End If
      Else
         Exit Do
      End If
   Loop
   RemoveParenthContent = s
End Function

答案4

得分: 1

以下是代码部分的翻译:

Function removeParanthesisAndContent(strP As String) As String
    Dim arr1, arr2, i As Long, strTxt As String

    arr1 = Split(strP, "(")
    If UBound(arr1) = 0 Then
        removeParanthesisAndContent = strP
    Else
        strTxt = arr1(0)
        For i = 1 To UBound(arr1)
            arr2 = Split(arr1(i), ")")
            strTxt = strTxt & arr2(1)
        Next i
        removeParanthesisAndContent = strTxt
    End If
End Function

请注意,我已经将 " 替换为引号字符 ",以便代码在正常的编程环境中运行。

英文:

Even if the question has been answered, please also try the next function using arrays:

Function removeParanthesisAndContent(strP As String) As String
    Dim arr1, arr2, i As Long, strTxt As String

    arr1 = Split(strP, "(")
    If UBound(arr1) = 0 Then
        removeParanthesisAndContent = strP
    Else
        strTxt = arr1(0)
        For i = 1 To UBound(arr1)
            arr2 = Split(arr1(i), ")")
            strTxt = strTxt & arr2(1)
        Next i
        removeParanthesisAndContent = strTxt
    End If
End Function

It is able to process a string with as many parenthesis pairs as you want and it can be tested in the next way:

Sub ReplaceParanthesisAndInBetween()
    Const s = "Lorem Ipsum"
    Const s1 = "Lorem(abcd) Ipsum"
    Const s2 = "Lorem(abc) Ipsum(ssss) dolor sit"
    Const s3 = "Lorem (xyz)Ipsum(123) dolor sit(321) amet"
    Const s4 = "Lorem (xyz)Ipsum(123) dolor sit(321) amet(xyz)"
    
    Debug.Print removeParanthesisAndContent(s)
    Debug.Print removeParanthesisAndContent(s1)
    Debug.Print removeParanthesisAndContent(s2)
    Debug.Print removeParanthesisAndContent(s3)
    Debug.Print removeParanthesisAndContent(s4)
End Sub

答案5

得分: 0

以下是翻译好的部分:

只是为了看到 FilterXML 的威力,请测试下面的函数。它应该非常快速和紧凑(只有一行代码...):

Function removeParXML(txt As String) As String
    With Application
        removeParXML = Join(.Transpose(.FilterXML("<t><s>" & VBA.Replace(VBA.Replace(txt, "(", "</s><p>"), ")", "</p><s>") & "</s></t>", "//s[count(node())>0]")))
    End With
End Function

您可以使用以下方式进行测试:

Sub testRemoveParXML()
    Const strText = "Lorem (xyz)Ipsum(123) dolor sit(321) amet(qwerty)"
    
    Debug.Print removeParXML(strText)
End Sub

由于无法添加清晰的注释以便易于理解函数,我将放置另一个函数,将其拆分为较小的部分,以便进行注释和理解(我认为):

Function removeParXMLDetails(txt As String) As String
    Dim strXML As String: strXML = "<t><s>" & VBA.Replace(VBA.Replace(txt, "(", "</s><p>"), ")", "</p><s>") & "</s></t>"
    
    Debug.Print strXML '创建一个XML字符串,其中包含主根标签<t></t>和节点<s></s>,用于保留字符串和
                       '用于括号内容的<p></p>标签(不要提取)。在即时窗口中查看它
                                       
    Dim arrXML: arrXML = Application.FilterXML(strXML, "//s[count(node())>0]") '它返回所有非空<s></>节点的二维(一列)数组!
                                                                                                                                         '如果字符串不包含最后的括号对,
                                                                                                                                         '返回一个空节点,'XPath查询字符串'(第二个参数)
                                                                                                                                         '只能是"//s"以返回所有节点(而不是"//s[count(node())>0]")...
    '为了连接数组内容,需要对其进行转置(以变为一维类型):
    removeParXMLDetails = Join(Application.Transpose(arrXML))
End Function

为了测试它,您只需要在上面的测试子过程的末尾添加以下代码行:

Debug.Print removeParXMLDetails(strText)

非常感谢 @T.M.,他引发了我对于理解自Excel 2013版本以来存在的FilterXML的强大功能的兴趣和挑战!

英文:

Just to see the power of FilterXML, please test the next function. It should be very fast and compact (only a code line...):

Function removeParXML(txt As String) As String
    With Application
        removeParXML = Join(.Transpose(.FilterXML(&quot;&lt;t&gt;&lt;s&gt;&quot; &amp; VBA.Replace(VBA.Replace(txt, &quot;(&quot;, &quot;&lt;/s&gt;&lt;p&gt;&quot;), &quot;)&quot;, &quot;&lt;/p&gt;&lt;s&gt;&quot;) &amp; &quot;&lt;/s&gt;&lt;/t&gt;&quot;, &quot;//s[count(node())&gt;0]&quot;)))
    End With
End Function

You can test it using:

Sub testRemoveParXML()
    Const strText = &quot;Lorem (xyz)Ipsum(123) dolor sit(321) amet(qwerty)&quot;
    
    Debug.Print removeParXML(strText)
End Sub

Not being possible to place eloquent comments to easily understand the function, I will place another one, split in smaller slices able to be commented and understood (I think):

Function removeParXMLDetails(txt As String) As String
    Dim strXML As String: strXML = &quot;&lt;t&gt;&lt;s&gt;&quot; &amp; VBA.Replace(VBA.Replace(txt, &quot;(&quot;, &quot;&lt;/s&gt;&lt;p&gt;&quot;), &quot;)&quot;, &quot;&lt;/p&gt;&lt;s&gt;&quot;) &amp; &quot;&lt;/s&gt;&lt;/t&gt;&quot;
    
    Debug.Print strXML &#39;it creates a XML string having the main root tag &lt;t&gt;&lt;/t&gt; and nodes &lt;s&gt;&lt;/s&gt; for strings to REMAIN and
                       &#39;&lt;p&gt;&lt;/p&gt; tag for paranthesis content (to NOT be extracted). See it in Immediate Window
                                       
    Dim arrXML: arrXML = Application.FilterXML(strXML, &quot;//s[count(node())&gt;0]&quot;) &#39;it returns a 2D (one column) array of all NOT EMPTY &lt;s&gt;&lt;/&gt; nodes!
                                                                                                                                         &#39;If the string would not contain a last parenthesis pair,
                                                                                                                                         &#39;returning an EMPTY NODE,  &#39;XPath query string&#39; (second parameter)
                                                                                                                                         &#39;can only be &quot;//s&quot; to return ALL nodes (instead of &quot;//s[count(node())&gt;0]&quot;)...
    &#39;In order to join the array contents it needs to be Transposed (to become 1D type):
    removeParXMLDetails = Join(Application.Transpose(arrXML))
End Function

In order to test it, you should only add at the end of the above testing sub the next code line:

   Debug.Print removeParXMLDetails(strText)

Many thanks to @T.M. who raised in me the interest and challenge of understanding the power of FilterXML, existing in Excel since 2013 version!

huangapple
  • 本文由 发表于 2023年6月13日 01:55:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76459175.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定