Regex 这个正则表达式在Excel单元格中查找文本的编号行是否可以改进以避免错误匹配?
我有一个大的电子表格,其中有些单元格可能包含许多行文本,有些是编号的,有些不是。我的目标是将这些单独编号的“项目”提取到单独的单元格中 例如,输入单元格可能包含以下内容(在“s”之间): "一,。甲方已完成。Regex 这个正则表达式在Excel单元格中查找文本的编号行是否可以改进以避免错误匹配?,regex,excel,vba,Regex,Excel,Vba,我有一个大的电子表格,其中有些单元格可能包含许多行文本,有些是编号的,有些不是。我的目标是将这些单独编号的“项目”提取到单独的单元格中 例如,输入单元格可能包含以下内容(在“s”之间): "一,。甲方已完成。 2./3. 乙方按照图纸805/12施工。 使用ITP 675/24 4、拟聘用丙方。” 请注意,项目编号从一行的开头开始,或使用“/”。数字后面总是跟一个“.”(点)。点后面可能有一些空格或没有空格,项目的文本可能会分散到多行 在上述输入单元上运行时,所需输出为: 第1单元:“1。甲方已
2./3. 乙方按照图纸805/12施工。
使用ITP 675/24 4、拟聘用丙方。” 请注意,项目编号从一行的开头开始,或使用“/”。数字后面总是跟一个“.”(点)。点后面可能有一些空格或没有空格,项目的文本可能会分散到多行 在上述输入单元上运行时,所需输出为: 第1单元:“1。甲方已完成。”
第二单元:“2。乙方按照图纸805/12施工。
使用ITP 675/24。“
第三单元:“3。乙方按照图纸805/12施工。
使用ITP 675/24。“
第四单元:“4.丙方被聘用。” 我一直在VBA中使用RegExp类对象,如下所示。这允许我精确定位项目的开头,然后提取这些点之间的文本(或字符串的结尾): 这通常是有效的,但我从行的末尾得到了不需要的匹配,如“/12.”和“/24.”。我如何更改正则表达式以排除这些匹配
请注意,我捕获“/”的出现,以确定项目编号是否需要从下一个编号继承文本。在这种情况下,项目2从项目3继承文本。但我不确定是否有更好的方法来管理此挑战。给定您的数据,类似于
(?:\d+\.\/)\124;(?:\ d+\.[\s\s]+(?=(?:\x0A+\d+\)\124$)的模式
将收集每条线的起点(编号段)和线的其余部分(编号段)
如果行号后面跟有/
,它只收集行号,这样您就可以通过测试最右边的字符是否是/
,来判断是否需要填充。填充结果数组后,我们从下到上循环遍历它,并决定需要在何处填充空格
这里是另一种方法,使用正则表达式。
如前所述,公式返回一个垂直数组。如果O365具有动态数组,则会溢出结果。如果没有,则可以通过在多个单元格上输入公式作为数组公式或使用索引函数来检索结果
Option Explicit
Function foo(s) As String()
Dim RE As RegExp, MC As MatchCollection, M As Match
Const sPat As String = "(?:\d+\.\/)|(?:\d+\.[\s\S]+?(?=(?:\x0A+\d+\.)|$))"
Dim sTemp() As String, I As Long
Set RE = New RegExp
With RE
.Global = True
.MultiLine = False
.Pattern = sPat
If .Test(s) = True Then
Set MC = .Execute(s)
ReDim sTemp(1 To MC.Count, 1 To 1) '2D array for vertical results
I = 0
For Each M In MC
I = I + 1
sTemp(I, 1) = M
Next M
End If
For I = UBound(sTemp, 1) - 1 To LBound(sTemp, 1) Step -1
If Right(sTemp(I, 1), 1) = "/" Then
sTemp(I, 1) = Replace(sTemp(I, 1), "/", "") & Mid(sTemp(I + 1, 1), InStr(sTemp(I + 1, 1), ".") + 1, 999)
End If
Next I
foo = sTemp
End With
End Function
正则表达式解释
提取行
选项:^$换行时不匹配
(?:\d+\.\/)
(?:\d+\.\/)
\d+
+
\。
\/
(?:\d+\.\s\s]+?(?=(?:\x0A+\d+\.\124;$)
(?:\d+\.\s\s]+?(?=(?:\x0A+\d+\.\124;$)
\d+
+
\。
[\s\s]+?
+?
\s
\S
(?=(?:\x0A+\d+\)\124;$)
(?:\x0A+\d+\)
(?:\x0A+\d+\)
\x0A+
+
\d+
+
\。
$
$
使用Try
^(\d+\.(?:/\d+\.)*)(?!$)
创建,请参阅想知道.Pattern=“^[\d+\.\/]+“
是否有助于您的事业?它将初始部分捕获为单个块,然后可以检查并拆分为“/”。一个没有RegExp的更简单的解决方案也会起作用。@shrivallabha.redij这是一个合理的起点,修改后删除“+”(按照您编写它的方式,它会被视为一个文字字符),因此^[\d\.\/]+
。但它落在了像“^26/05/2020:”这样的文本段落中,它认为日期是一行编号的文本。诚然,在我开发了一个脚本并真正开始研究数据的可能性之前,我不知道这是一种可能性。哇,付出了巨大的努力,谢谢。我将使用@Wiktor Stribiżew建议的一个修改版本,因为我已经有代码可以去掉项目编号点击之间的字符串段。因此,以下内容对我有用:(?:^(\d+))\124;(?:\/(\ d+)(?!$)
@Grevillea2020很高兴为您提供帮助。你确实要求其他选择。顺便说一句,将上面评论中的正则表达式应用到数据中,它似乎会在第三行结尾处拾取\24.
。如果\24.
后面和结尾之前有一些字符,这是正确的,该死,我的用户偶尔会这样做。你的正则表达式似乎也有这个问题。我通过将最后的$
替换为\z
@Grevillea2020来修复它,或者您可以按照我演示的方式,使用Multiline=False
@Grevillea2020来完成它。此外,我不相信VBA/VBScript支持\z
。你在用别的东西吗?
Option Explicit
Function foo(s) As String()
Dim RE As RegExp, MC As MatchCollection, M As Match
Const sPat As String = "(?:\d+\.\/)|(?:\d+\.[\s\S]+?(?=(?:\x0A+\d+\.)|$))"
Dim sTemp() As String, I As Long
Set RE = New RegExp
With RE
.Global = True
.MultiLine = False
.Pattern = sPat
If .Test(s) = True Then
Set MC = .Execute(s)
ReDim sTemp(1 To MC.Count, 1 To 1) '2D array for vertical results
I = 0
For Each M In MC
I = I + 1
sTemp(I, 1) = M
Next M
End If
For I = UBound(sTemp, 1) - 1 To LBound(sTemp, 1) Step -1
If Right(sTemp(I, 1), 1) = "/" Then
sTemp(I, 1) = Replace(sTemp(I, 1), "/", "") & Mid(sTemp(I + 1, 1), InStr(sTemp(I + 1, 1), ".") + 1, 999)
End If
Next I
foo = sTemp
End With
End Function
(?:\d+\.\/)|(?:\d+\.[\s\S]+?(?=(?:\x0A+\d+\.)|$))