【问题标题】:How to check that a regular expression has matched a string completely, i.e. - the string did not contain any extra character?如何检查正则表达式是否完全匹配字符串,即 - 字符串不包含任何额外字符?
【发布时间】:2010-10-07 07:29:31
【问题描述】:

我有两个问题:

1) 我有一个正则表达式 ([A-Z][a-z]{0,2})(\d*),我正在使用 Python 的 re.finditer() 来匹配适当的字符串。我的问题是,我只想匹配不包含额外字符的字符串,否则我想引发异常。

我想捕捉以下模式: - 大写字母,后跟 0、1 或 2 个小写字母,后跟 0 个或多个数字。

模式代表一个化学式,即原子后跟它的出现次数。我想将原子及其出现次数放入字典中,因此我需要将原子(大写字母后跟 0、1 或 2 个小写字母)和数字分开,但请记住它们属于一起。

例子:

C6H5Fe2I   # this string should be matched successfully. Result: C6 H5 Fe2 I
H2TeO4     # this string should be matched successfully Result: H2 Te O4
H3PoooO5   # exception should be raised
C2tH6      # exception should be raised

2)第二个问题是如果输入字符串错误,我应该引发什么样的异常。

谢谢你,托马斯

【问题讨论】:

  • 我无法理解您要匹配的模式。
  • 如果我错了,请纠正我。给定C6H5Fe2I,您需要以下内容:C6、H5、Fe2 和 I。不是吗?
  • @JoshD:他试图匹配的模式在问题的第一行。
  • 如果模式不匹配,您似乎想引发异常。这是为什么呢?
  • @Mark Byers,该模式与他所显示的不匹配,嗯,不是直接匹配。我现在明白我的意图了。

标签: python regex exception-handling error-handling


【解决方案1】:

您可以使用以下几种不同的方法:

比较长度

  • 求原始字符串的长度。
  • 对匹配字符串的长度求和。
  • 如果两个数字不同,则有未使用的字符。

请注意,如果您想避免两次解析字符串,也可以将此方法与现有代码结合使用,而不是将其作为额外步骤。

整个字符串的正则表达式

你可以检查这个正则表达式是否匹配整个字符串:

^([A-Z][a-z]{0,2}\d*)*$

(Rubular)

标记化

您可以使用以下正则表达式来标记原始字符串:

[A-Z][^A-Z]*

然后检查每个标记,看它是否与你原来的正则表达式匹配。

【讨论】:

  • 第一个正则表达式:为什么以^ 开头?为什么以$ 而不是\Z 结尾?第二个*不应该是+吗?
【解决方案2】:

大写字母,后跟 0、1 或 2 小写字母,后跟 0 或更多 数字

那好吧。

/^([A-Z][a-z]{0,2}\d*)+$/

这里的区别在于^$ 中的额外分组(foo)+ 允许您捕获模式foo N 次。

没有全局标志?猜猜你将不得不再次在模式上拆分该正则表达式的结果。

【讨论】:

  • 上次我检查时,python 没有 G 标志,所以你不会得到单独的匹配项..
  • 哇,真的吗?你们是怎么应对的?
  • 不过有一个 findall() 函数。并且 sub() 总是被认为是全局的。一般不会有问题
  • findall() 是 /g 的良好替代品,如果您有包含换行符的文本块,您可能还想使用修饰符 re.MULTILINE 和/或 re.DOTALL。
【解决方案3】:

您是否需要提取每个单独的部分进行处理,或者只是匹配输入验证?如果您只需要匹配验证,请尝试^([A-Z][a-z]{0,2}\d*)+$。

【讨论】:

    【解决方案4】:

    你需要稍微不同的正则表达式:

    ^([A-Z][a-z]{0,2})(\d*)$
    

    但是,它不会匹配您的任何示例字符串。您需要更好地描述为什么这些字符串应该匹配。

    只是为了测试您是否可以使用整个字符串匹配:

    >>> re.match(r'(([A-Z][a-z]{,2})(\d*))+$', 'H2TeO4')
    <_sre.SRE_Match object at 0x920f520>
    >>> re.match(r'(([A-Z][a-z]{,2})(\d*))+$', 'H3PoooO5')
    >>> 
    

    我没有找到纯正则表达式解决方案,但这里是测试和收集匹配项的方法:

    >>> res = re.findall(r'([A-Z][a-z]{,2})(\d*)(?=(?:[A-Z][a-z]{,2}\d*|$))', s)
    >>> res
    [('C', '6'), ('H', '5'), ('Fe', '2'), ('I', '')]
    >>> ''.join(''.join(i) for i in res) == s
    True
    

    【讨论】:

    • 是的,完全正确。这是我尝试的第一件事,但我没有匹配任何东西。
    • @Manoj,与其争论 OP 可能想要或不想要什么,不如让 OP 澄清一下? SilentGhost 和 Michel 的答案在我看来都不错。
    • @Bart:请参阅我对 OP 问题的评论。我确实要求他澄清一下。
    • @Manoj,是的,我现在明白了。而且我看到 OP 现在通过指出预期的输出应该是什么来澄清他/她自己。我的观点是,在得到这个澄清之前,没有必要评论其他人的答案。
    • @Bart:关于不发布 cmets,我希望有所不同。我将我的 cmets 建立在“对他人做...”的基础上。如果我误读了一个问题并提供了一个答案(不符合要求),并且其他人看到并理解了它,那么我'd 愿意被告知。这将有助于 我 做出改变(过去也是如此)。
    【解决方案5】:
    >>> import re
    >>> reMatch = re.compile( '([A-Z][a-z]{0,2})(\d*)' )
    >>> def matchText ( text ):
            matches, i = [], 0
            for m in reMatch.finditer( text ):
                if m.start() > i:
                    break
                matches.append( m )
                i = m.end()
            else:
                if i == len( text ):
                    return matches
            raise ValueError( 'invalid text' )
    
    >>> matchText( 'C6H5Fe2I' )
    [<_sre.SRE_Match object at 0x021E2800>, <_sre.SRE_Match object at 0x021E28D8>, <_sre.SRE_Match object at 0x021E2920>, <_sre.SRE_Match object at 0x021E2968>]
    >>> matchText( 'H2TeO4' )
    [<_sre.SRE_Match object at 0x021E2890>, <_sre.SRE_Match object at 0x021E29F8>, <_sre.SRE_Match object at 0x021E2A40>]
    >>> matchText( 'H3PoooO5' )
    Traceback (most recent call last):
      File "<pyshell#6>", line 1, in <module>
        matchText( 'H3PoooO5' )
      File "<pyshell#3>", line 11, in matchText
        raise ValueError( 'invalid text' )
    ValueError: invalid text
    >>> matchText( 'C2tH6' )
    Traceback (most recent call last):
      File "<pyshell#7>", line 1, in <module>
        matchText( 'C2tH6' )
      File "<pyshell#3>", line 11, in matchText
        raise ValueError( 'invalid text' )
    ValueError: invalid text
    

    为了比上面的代码更清楚地回答您的第二个问题:ValueError 用于参数类型正确但值不正确的情况。因此,对于使用正则表达式的函数,显然是您可以选择的最佳选择。

    【讨论】:

      【解决方案6】:

      使用这种模式

      (([A-Z][a-z]{0,2})(\d*))+
      

      如果匹配,那就太好了!如果没有,那就处理它。如果不匹配,我认为没有理由引发异常。您必须提供更多信息。

      【讨论】:

      • 这将匹配空字符串,原始实现不接受。
      • @Darth Android:谢谢;改变了。
      • 如果应用程序需要格式良好的字符串,并且他将该字符串拆分为所需的块并注意到它是无效的,那么引发异常是最合适的做法。
      • @poke:我同意,如果明确说明,我会很高兴......我猜任何错误,甚至可以引发自定义错误。
      【解决方案7】:

      我不用正则表达式:

      tests= (
      'C6H5Fe2I',   # this string should be matched successfully. Result: C6 H5 Fe2 I
      'H2TeO4',     # this string should be matched successfully Result: H2 Te O4
      'H3PoooO5',   # exception should be raised
      'C2tH6')      # exception should be raised
      
      def splitter(case):
          case, original = list(case), case
          while case:
              if case[0].isupper():
                  result = case.pop(0)
              else:
                  raise ValueError('%r is not capital letter in %s position %i.' %
                                   (case[0], original, len(original)-len(case)))
              for count in range(2):
                  if case and case[0].islower():
                      result += case.pop(0)
                  else:
                      break
              for count in range(2):
                  if case and case[0].isdigit():
                      result += case.pop(0)
                  else:
                      break
              yield result
      
      for testcase in tests:
          try:
              print tuple(splitter(testcase))
          except ValueError as e:
              print(e)
      

      【讨论】:

        【解决方案8】:

        您可以使用 re.split 用不多的代码做到这一点——是的,没错,re.split。

        这里是the docs:

        反转您的问题:使用与有效 atom+count 匹配的分隔符模式拆分您的输入。有一个捕获组,以便保留分隔符字符串。如果输入字符串有效,则结果中的非分隔符都是空字符串。

        >>> tests= (
        ... 'C6H5Fe2I',
        ... 'H2TeO4',
        ... 'H3PoooO5',
        ... 'C2tH6',
        ... 'Bad\n')
        >>> import re
        >>> pattern = r'([A-Z][a-z]{0,2}\d*)'
        >>> for test in tests:
        ...     pieces = re.split(pattern, test)
        ...     print "\ntest=%r pieces=%r" % (test, pieces)
        ...     data = pieces[1::2]
        ...     rubbish = filter(None, pieces[0::2])
        ...     print "rubbish=%r data=%r" % (rubbish, data)
        ...
        
        test='C6H5Fe2I' pieces=['', 'C6', '', 'H5', '', 'Fe2', '', 'I', '']
        rubbish=[] data=['C6', 'H5', 'Fe2', 'I']
        
        test='H2TeO4' pieces=['', 'H2', '', 'Te', '', 'O4', '']
        rubbish=[] data=['H2', 'Te', 'O4']
        
        test='H3PoooO5' pieces=['', 'H3', '', 'Poo', 'o', 'O5', '']
        rubbish=['o'] data=['H3', 'Poo', 'O5']
        
        test='C2tH6' pieces=['', 'C2', 't', 'H6', '']
        rubbish=['t'] data=['C2', 'H6']
        
        test='Bad\n' pieces=['', 'Bad', '\n']
        rubbish=['\n'] data=['Bad']
        >>>
        

        【讨论】:

          【解决方案9】:

          为了验证,请尝试 如果您使用的是 .NET Framework

          ([A-Z][a-b]??[0-9]??)*
          

          其他的

          ([A-Z][a-b]?[a-b]?[0-9]?[0-9]?)*
          

          【讨论】:

          • (1) Python 也支持勉强量词,(2) 这两个正则表达式不等价,(3) 它们都是错误的。 [a-z]?? 最多匹配一个字母(OP 说最多可以有两个);而[0-9]?[0-9]? 最多匹配两位数(OP 对这些数字没有任何限制)。
          • (1)我对Python一无所知(2)那些正则表达式在.net Framework中是等价的,因为[a-z]??相当于 [a-z]?[a-z]?在 .net 框架中播种 a 和 z 之间的 0 到 2 个字符(3)关于数字替换 [0-9] 的无限?与 [0-9]*
          • "?? 匹配前一个元素零次或一次,但尽可能少。"所以你完全错了,多余的“?”后缀切换到适度(非贪婪)模式。
          【解决方案10】:

          通过匹配输入字符串中的模式并将匹配替换(替换)为空字符串(''),这可能是最简单的方法。现在,如果整个字符串变为空,那么它就是一个完全匹配! (如果有任何剩余,则不是完全匹配。)

          import re
          
          pattern = r'([A-Z][a-z]{0,2})(\d*)'
          
          input_text = "C6H5Fe2I" # Some input text
          output = re.sub(pattern, '', input_text)
          
          if output == '':
              print("[INFO] COMPLETE MATCH!")
              print("[FINDINGS]", re.findall(pattern, input_text))
          else:
              print("[INFO] NOT A COMPLETE MATCH.")
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-04-21
            • 1970-01-01
            • 1970-01-01
            • 2023-03-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多