正则表达式 - 在匹配之间捕获文本

时间:2016-01-17 18:36:37

标签: python regex

在下面的文字中,我尝试匹配一个数字后跟")"和数字后跟一段时间。我试图在比赛之间检索文字 示例:

  

" 1)有一个dsfsdfsd和2)还有另一个和另一个   情况下"

所以我想输出:["有一个dsfsdfsd和","还有另外一个",还有另一个案例"]

我使用了这个正则表达式:(?:\ d)| \ d。) 在末尾添加。*匹配整个字符串,我只希望它匹配

之间的单词

也在这个字符串中:

  

"我们将给出4.需要另外一个选项,6.99美元是一个   比特数"

我想只匹配4.而不是6.99

任何指针都将受到赞赏。谢谢。 [R

5 个答案:

答案 0 :(得分:1)

根据您的任务判断,匹配分隔符并使用re.split可能更容易(正如评论中bobblebubble所指出的那样)。

我提出了一个仅仅

\d+[.)]\B\s*

请参阅it in action (demo)

匹配1位或更多位数,然后是.),然后确保后面没有单词字母(数字,字母或下划线),然后匹配零个或多个空格

Python demo

import re
rx = r'\d+[.)]\B\s*'
test_str = "1) there is a dsfsdfsd and 2) there is another one and 3) yet another case\n\"we will give 4. there needs to be another option and 6.99 USD is a bit amount"
print([x for x in re.split(rx,test_str) if x])

答案 1 :(得分:1)

<强> tldr

  • 正则表达式是棘手的野兽,如果可能的话,你应该避免它们。
  • 如果你无法避免它们,那么请确保你有很多可能发生的边缘情况的测试用例。
  • 缓慢而系统地构建正则表达式,在每一步测试您的假设。
  • 如果这段代码将介绍生产,那么请写一些单元测试,向需要维护它的穷人灵魂解释思考过程

长版

正则表达式很挑剔。您最好的方法可能是以不同的方式解决问题。

例如,您的语言可能具有库函数,该函数允许您使用正则表达式拆分字符串以定义数字之间的内容。这样你就可以编写一个更简单的正则表达式来匹配数字和括号/点。

如果你仍然决定使用正则表达式,那么你需要非常有条理地构建正则表达式。很容易错过边缘案例。

所以,让我们一块一块地打破这个......

  1. 设置测试环境,以便快速试验正则表达式。
    • 这里有很多选项,具体取决于您的编程语言和操作系统。我有时使用的是:
      • 用于测试.Net正则表达式的Powershell窗口(注意:cli为您提供过去尝试的历史记录,因此如果您把事情搞得太糟糕,您可以回过头几步)。
      • 用于测试Python正则表达式的Python控制台(在命名捕获组的语法中与.Net正则表达略有不同)。
      • 一个用JavaScript来测试正则表达式的html页面
      • 一个在线或桌面正则表达式工具(我仍然使用Eric Gunnerson的古老正则表达式工作台,但我确定这些天有更好的选择)
    • 由于您没有指定语言或正则表达式版本,我将假设.Net正则表达式
  2. 创建单个测试字符串以测试更多种选项。
    • 您的目标是包含尽可能多的边缘案例。以下是我要使用的内容:"ab 1. there is a dsfsdfsd costing $6.99 and 2) there is another one and 3. yet another case 4)5) 6)10."
    • 请注意,我添加了一些您没有提及的额外案例:
      • 两个圆括号之间的空字符串:&#34; 4)&#34;和&#34; 5)&#34;
      • 两个圆括号之间的空白字符串:&#34; 5)&#34;和&#34; 6)&#34;
      • 圆括号和点号之间的空字符串:&#34; 6)&#34;和&#34; 10。&#34;
      • 虚线后的空字符串&#34; 10。&#34;在字符串的末尾
      • 在第一个数字
      • 之前应该忽略的随机文本和空格
    • 我将在这里做一些假设,您需要根据实际要求进行调整:
      • 您想要在点或圆括号后捕捉空白区域。
      • 您希望在下一个虚线数字或圆括号之前捕获空格。
      • 你的数字可能会超过9,所以我已经包含了&#34; 10&#34;在测试案例中。
      • 你想在最后捕获空字符串,例如在&#34; 10。&#34;
      • 之后
    • 注意:
      • 通过这个测试案例的思考迫使您对自己的要求更加严格。
      • 当您手动测试正则表达式时,它还可以帮助您提高效率。
      • 但是,假设您没有遵循TDD方法。如果你是,那么你应该做一些不同的事情......分别为每个场景创建单元测试,并逐步使用正则表达式。
      • 此测试字符串并不涵盖所有情况。例如,测试字符串中没有换行符或制表符。此外,它不能在最后的圆括号后面测试一个空字符串。
  3. 首先得到正则表达式,只捕获圆括号和虚线括号。
    • 不要担心6.99美元的边缘情况。
    • 暂时从正则表达式中删除&#34;(?:&#34;非捕获组语法:"\d)|\d."
    • 这甚至不会解析,因为你有一个未转义的圆括号。
    • 修订后的字符串是"\d\)|\d.",它解析,但也匹配&#34; 99&#34;你可能并不期待。那是因为你忘了逃避&#34;。&#34;
    • 修订后的字符串为"\d\)|\d\."。这不再匹配&#34; 99&#34;,但它现在匹配&#34; 0。&#34;最后而不是&#34; 10。&#34;。这是因为它假设数字只是单个数字。
    • 以下字符串似乎有效:"\d+\)|\d+\."
    • 是时候处理那个讨厌的&#34; 6.99美元&#34;现在...
  4. 修改正则表达式,使其不捕获浮点数。
    • 您需要使用负前瞻模式来防止数字位于小数点后面。
    • 结果:"\d+\)|\d+\.(?!\d)"
    • 计算这会产生多少匹配。您将使用此号码检查以后的结果。
    • 提示:将正则表达式模式保存在某处。您希望能够在无法修复正则表达式模式的情况下重新使用它。
    • 如果您找到了字符串拆分功能,那么您现在应该使用它并避免后续的复杂性。 [我在最后添加了一个示例。]
    • 简单就是更好,但我会继续使用更长的解决方案,以便能够保持控制正则表达式的方法,开始变得非常复杂
  5. 决定如何排除该模式
    • 您在问题中使用了非捕获组模式,即&#34;(?:&#34;
    • 这种方法可行。但它有点麻烦,因为你需要有一个捕获组,而不是你要寻找的。
    • 如果你的整个模式与你想要的相匹配会更好。
    • 所以将数字模式包装在模式后面的零宽度正面外观中(如果您的语言支持它),即&#34;(?&lt; =&#34;。
    • 这会检查模式,但不会将其包含在捕获的内容中。
    • 所以现在你的正则表达式如下:"(?<=\d+\)|\d+\.(?!\d))"
  6. 测试一下!
    • 单独测试这个似乎很愚蠢 - 所有的匹配都是空字符串。
    • 无论如何要这样做。你想要理智地检查每一步。
    • 确保它仍然产生与步骤4相同的匹配数。
  7. 决定如何匹配数字之间的文本。
    • 你正确地提到&#34;。*&#34;将匹配整个字符串,而不仅仅是两者之间的部分。
    • 这是一个巧妙的技巧,允许您重复使用步骤5中的模式来获取文本。
    • 首先匹配下一个字符
    • 诀窍是你要匹配任何角色,除非它是下一个数字的开头
    • 这听起来像是一个负向的前瞻模式:"(?!"
    • 设X是您在步骤4中保存的模式。匹配单个字符将如下所示:"(?!X)."
    • 你想匹配很多这些角色。因此,将该模式放入非捕获组并重复它:"(?:(?!X).)*"
      • 这假设您要捕获空文本。
      • 如果您不是,请更改&#34; *&#34;到&#34; +&#34;。
      • 提示:这是一种常见的模式,您希望在将来粘贴时使用不同的模式代替X
      • 我使用了非捕获组而不是普通组,因此您也可以将此模式嵌入到您关注捕获组的正则表中
    • 结果模式:"(?:(?!\d+\)|\d+\.(?!\d)).)*"
      • 我建议单独测试这个模式以查看它的作用
  8. 现在将第5和第7部分放在一起:"(?<=\d+\)|\d+\.(?!\d))(?:(?!\d+\)|\d+\.(?!\d)).)*"
    • 测试它!
  9. 单元测试!
    • 如果要投入生产,那么请编写大量的单元测试来解释这个思维过程的每一步
    • 对将来必须保持你的正则表达的可怜的灵魂表示同情!
    • 以人为本的权利
    • 我建议您在日历中添加一张便条,以便在6个月后返回此代码。时间,并确保你仍然可以单独从单元测试中理解它!
  10. 重构
    • 在六个月内&#39;时间,如果你不能再理解代码,可以使用你新发现的洞察力(和激励)来解决问题而不使用正则表达式(或只是非常简单的表达式)
  11. <强>附录

    作为使用字符串拆分函数来逃避更简单的正则表达式的示例,这是Powershell中的解决方案:

    $string = 'ab   1. there is a dsfsdfsd costing $6.99 and 2) there is another one and 3. yet another case 4)5)   6)10.'
    $pattern = [regex] '\d+\)|\d+\.(?!\d)'
    $string -split $pattern | select-object -skip 1
    

答案 2 :(得分:0)

使用boolean equals(Object obj);修饰符尝试以下正则表达式:

g

示例:https://regex101.com/r/kB1xI0/3

([A-Za-z\s\-_]+|\d(?!(\)|\.)\D)|\.\d) 自动匹配所有字母字符+空格

[A-Za-z\s\-_]+匹配任何数字数字序列,后面没有右括号\d(?!(\)|\.)\D)或十进制值()

.99匹配任何句号后跟数字。

答案 3 :(得分:0)

我使用了这种模式:

(?<=\d.\s)(.*?)(?=\d.\s)

demo

这将查找任何数字,任何字符和空格之间的内容。

修改:更新模式以更好地处理货币问题和行结束:

这是标志'g'

(?<=[0-9].\s)(.*?)(?=\s[0-9].\s|\n|\r)

Demo 2

答案 4 :(得分:0)

import re


s = "1) there is a dsfsdfsd and 2) there is another one and 3) yet another case"
s1 = "we will give 4. there needs to be another option and 6.99 USD is a bit amount"

regex = re.compile("\d\)\s.*?|\s\d\.\D.*?")


print ([x for x in regex.split(s) if x])
print regex.split(s1)

输出:

['there is a dsfsdfsd and ', 'there is another one and ', 'yet another case']
['we will give', 'there needs to be another option and 6.99 USD is a bit amount']