【问题标题】:Get every word ending with dot using Regex/VBA使用 Regex/VBA 获取每个以点结尾的单词
【发布时间】:2022-11-10 23:01:53
【问题描述】:

我正在使用 excel 2019,我正在尝试从一堆乱七八糟的文本单元格中提取任何(最多 5 个)以 . 之后的点结尾的单词。

这是我要解析/清理的文本示例 `

some text [asred.] ost. |Monday - Ribben (ult.) lot. ac, sino. other maybe long text; collan. 

`

我希望得到这个: ost. ult. lot. sino. collan.

我正在使用在互联网上某个地方找到的这个功能,它似乎可以完成这项工作: `

Public Function RegExtract(Txt As String, Pattern As String) As String

With CreateObject("vbscript.regexp")
    '.Global = True
    .Pattern = Pattern
    If .test(Txt) Then
        RegExtract = .Execute(Txt)(0)
    Else
        RegExtract = "No match found"
    End If
End With

End Function

`

我从一个空单元格中调用它: =RegExtract(D2; "([\]])(\s\w+[.]){0,5}")

这是我第一次使用正则表达式,所以我可能在专家眼中做了可怕的事情。

所以这是我的表达: ([]])(\s\w+[.]){0,5}

现在它只返回 ] ost.

这比我在第一种正则表达式方法中所期望的要多得多,但是:

  1. 我无法摆脱第一个 ] ,因为 \K 在 excel 中不起作用,所以它需要在文本块内找到我有用的位开始的位置。我以后可能会“找到并替换”它作为一个聪明的野蛮人,但我想知道如何做到干净,如果有任何干净的方法存在的话:)

    2)我不明白迭代器如何工作以获取所有“最多 5 次发生”:我期待第二组之后的 {0,5} 完全意味着:“再次重复前一组,直到文本块结束(或者直到你成功完成 5 次)”。

    感谢您的时间 :)

    --在JdvD接受记录后添加--

    在第一次出现右括号之后,我正在使用这种模式来获取所有以点结尾的单词。 JdvD 建议的模式是同一字符的最后一次出现。

    "^\]|(\w+\.\s?)|."
    

    我什至在我的 regExtract 函数中遗漏了一些东西:通过 for 循环的匹配数组,假设正则表达式引擎已经将匹配存储为唯一字符串。

【问题讨论】:

    标签: excel regex vba regex-group


    【解决方案1】:

    正则表达式匹配:

    除了@RonRosenfeld 给出的答案之外,还可以应用一些人所说的'The Best Regex Trick Ever',这意味着首先匹配你的别想要然后匹配你的做想要在一个捕获组中。例如:

    ^.*]|(w+.)
    

    请参阅在线demo,简而言之,这意味着:

    • ^.*] - 匹配从字符串开头到最后出现的右方括号的 0+ 个(贪婪)字符;
    • | - 或;
    • (w+.) - 捕获组持有 1+(贪婪)以点结尾的单词字符。

    以下是它在 UDF 中的工作方式:

    Sub Test()
    
    Dim s As String: s = "some text [asred.] ost. |Monday - Ribben (ult.) lot. ac, sino. other maybe long text; collan. "
    
    Debug.Print RegExtract(s, "^.*]|(w+.)")
    
    End Sub
    
    '------
    
    'The above Sub would invoke the below function as an example.
    'But you could also invoke this through: `=RegExtract(A1,"^.*]|(w+.)")`
    'on your sheet.
    
    '------
    
    Public Function RegExtract(Txt As String, Pattern As String) As String
    
    Dim rMatch As Object, arrayMatches(), i As Long
    
    With CreateObject("vbscript.regexp")
        .Global = True
        .Pattern = Pattern
        If .Test(Txt) Then
            For Each rMatch In .Execute(Txt)
                If Not IsEmpty(rMatch.SubMatches(0)) Then
                    ReDim Preserve arrayMatches(i)
                    arrayMatches(i) = rMatch.SubMatches(0)
                    i = i + 1
                End If
            Next
            RegExtract = Join(arrayMatches, " ")
        Else
            RegExtract = "No match found"
        End If
    End With
    
    End Function
    

    正则表达式替换:

    根据您想要的输出,也可以使用替换功能。您必须将任何剩余的字符与另一个替代字符匹配。例如:

    ^.*]|(w+.s?)|.
    

    请参阅在线demo,简而言之,这意味着我们添加了另一种选择,它只是任何单个字符。第二个小补充是我们添加了一个选项可选的第二种选择中的空格字符s?。

    Sub Test()
    
    Dim s As String: s = "some text [asred.] ost. |Monday - Ribben (ult.) lot. ac, sino. other maybe long text; collan. "
    
    Debug.Print RegReplace(s, "^.*]|(w+.s?)|.", "$1")
    
    End Sub
    
    '------
    
    'There are now 3 parameters to parse to the UDF; String, Pattern and Replacement.
    
    '------
    
    Public Function RegReplace(Txt As String, Pattern As String, Replacement) As String
    
    Dim rMatch As Object, arrayMatches(), i As Long
    
    With CreateObject("vbscript.regexp")
        .Global = True
        .Pattern = Pattern
        RegReplace = Trim(.Replace(Txt, Replacement))
    End With
    
    End Function
    

    请注意,我使用Trim() 删除可能的尾随空格。


    RegexMatch 和 RegexReplace 目前都将返回一个字符串来清理输入,但前者确实为您提供了处理 arrayMatches() 变量中的数组的选项。

    【讨论】:

    • 非常感谢您详尽的回答和巧妙的技巧!我可以看出该函数按预期工作,但我注意到在要清理的文本块中,有时我还有其他方括号。是否可以修复表达式以在第一次出现 ] 之前否定所有内容(不是现在的最后一次出现)?谢谢!
    • 傻我!我只需要从您的否定模式中删除 .* ! :)
    • @Serbiss 我认为您应该使用 ^.*?] 将 ^.*] 更改为惰性模式
    • 您认为使用一种解决方案 (^]) 比另一种解决方案 (^.*?]) 有什么好处吗?两者似乎都有效
    【解决方案2】:

    您可以使用以下正则表达式

    ([a-zA-Z]+)。

    让我稍微解释一下。

    [a-zA-Z] ----> 这将查找包含从 a 到 z 和 A 到 Z 的任何字母的任何内容,但它只匹配第一个字母。

    + ----> 用这个你告诉它匹配所有字母,直到它找到不是从 a 到 z 和 A 到 Z 的字母的东西

    . ---->有了这个,你只是在寻找 .在比赛结束时

    这里example

    希望这是您正在寻找的。

    【讨论】:

    • 感谢您的回答,这确实是另一种方法(但我仍然需要找到第一个右方括号)
    【解决方案3】:

    有一种方法可以返回从某个模式开始的字符串中的所有匹配项。但我现在想不起来了。

    与此同时,似乎最简单的方法是删除第一个 ] 之前的所有内容,然后将 Regex 应用于其余部分。

    例如:

    Option Explicit
    Sub findit()
      Const str As String = "some text [asred.] ost. |Monday - Ribben (ult.) lot. ac, sino. other maybe long text; collan."
      Dim RE As RegExp, MC As MatchCollection, M As Match
      Dim S As String
      Dim sOutput As String
      
    S = Mid(str, InStr(str, "]"))
    
    Set RE = New RegExp
    With RE
        .Pattern = "w+(?=.)"
        .Global = True
        If .Test(S) = True Then
            Set MC = .Execute(S)
            For Each M In MC
                sOutput = sOutput & vbLf & M
            Next M
        End If
    End With
    
    
    MsgBox Mid(sOutput, 2)
    
    End Sub
    

    您当然可以通过使用计数器而不是 For each 循环将匹配数限制为 5

    【讨论】:

    • 谢谢你的回答,我没想到一个 for 循环来存储所有的比赛!我将 JvdV 标记为最佳答案,因为它涵盖了所有内容。
    猜你喜欢
    • 2018-01-15
    • 1970-01-01
    • 2019-12-02
    • 2022-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-31
    • 1970-01-01
    相关资源
    最近更新 更多