Python,正则表达式匹配数字,x,xxx,xxx,但不是xx,xx,x,

问题描述:

第一次发布,我潜伏了一会儿,对这里的帮助社区感到非常兴奋。Python,正则表达式匹配数字,x,xxx,xxx,但不是xx,xx,x,

因此,由铝Sweigart

“自动无聊的东西”这样做,需要我建立在标准的数字格式发现号的正则表达式的锻炼工作。三位数字,逗号,三位数字,逗号等...

所以希望匹配1,234和23,322和1,234,567和12,但不是1,23,1或1111或其他愚蠢的东西。

我有以下几点。

import re 

testStr = '1,234,343' 
matches = [] 
numComma = re.compile(r'^(\d{1,3})*(,\d{3})*$') 

for group in numComma.findall(str(testStr)): 
    Num = group 
    print(str(Num) + '-')   #Printing here to test each loop 
    matches.append(str(Num[0])) 

#if len(matches) > 0: 
# print(''.join(matches)) 

,输出这个....

( '1',” 343' ) -

我不知道为什么中间的” 234" 被跳过。我相信,正则表达式有什么问题。只是似乎无法把我的头围绕在这一个。

任何帮助或解释将不胜感激。

跟随编辑。因此,在遵循我可以吸收的所有建议之后,我将它完美地用于多种输入。

import re 

testStr = '1,234,343' 
numComma = re.compile(r'^(?:\d{1,3})(?:,\d{3})*$') 

Num = numComma.findall(testStr) 
print(Num) 

给我....

[ '1234343']

太好了!但!什么时候我的字符串输入更改为类似

“1234343和12345”

相同的代码返回....

[]

哎呀...大声笑,这是有趣的, 我必须承认。

因此,练习的目的是能够最终扫描一段文本并挑选出这种格式的所有数字。任何见解?我认为这将增加一个额外的元组,而不是返回一个空的...

跟进编辑:

所以,一天后(一直忙于3个女儿和蜂蜜待办事项列表),我已经终于能够坐下来检查我收到的所有帮助。这是我想出来的,它看起来工作得很好。包括评论为我自己的个人理解。再次感谢Blckknght,Saleem,mhawke和BHustus。

我的最终代码:

import re 

testStr = '12,454 So hopefully will match 1,234 and 23,322 and 1,234,567 and 12 but not 1,23,1 or ,,1111, or anything else silly.' 

numComma = re.compile(r''' 
    (?:(?<=^)|(?<=\s)) # Looks behind the Match for start of line and whitespace 
    ((?:\d{1,3})  # Matches on groups of 1-3 numbers. 
    (?:,\d{3})*)  # Matches on groups of 3 numbers preceded by a comma 
    (?=\s|$)''', re.VERBOSE) # Looks ahead of match for end of line and whitespace 

Num = numComma.findall(testStr) 
print(Num) 

将返回:

['12,454' , '1,234','23,322' , '1,234,567', '12']

再次感谢!我在这里有这样一个积极的第一次发布经验,令人惊叹。=)

+0

你为什么使用findall? – Alex

+0

这不是问题,但你的正则表达式是错误的。第一个'*'是一个错误,它允许模式匹配非标准格式的东西:例如''1234''和'',123''。 –

+0

我使用的是findall,因为我是一个完全彻底的新手,摸索着他的方式:)我会阅读不同的方法,并遵循这里流动的建议。 –

问题是:

正则表达式匹配将返回每个组的元组项。 但是,区分捕获很重要。由于您只有两个以括号分隔的组,因此匹配将始终为二元组:第一组和第二组。但第二组匹配两次。

1:第一组,捕获
,234 :第二组,捕获
,343秒组,这意味着它覆盖,234

不幸的是,似乎香草Python没有一种方法来访问比上一个类似.NET的正则表达式实施方式与其他任何一个组捕获。 但是,如果你只是想获得具体的数字,你最好的选择是使用re.search(number)。如果它返回一个非None值,那么输入字符串是一个有效的数字。否则,它不是。

此外:A test在您的正则表达式。请注意,正如Paul Hankin所说,测试用例6和7虽然不应该匹配,但由于第一个捕获组的第一个*会使初始组匹配任意次数,所以测试用例6和7也是如此。否则,你的正则表达式是正确的。 Fixed version.

回应编辑:
原因,现在您正则表达式在您的正则在返回一个空集“和”是因为^和$锚。正则表达式开头的^ anchor表示'这个点需要在一个字符串的开头'。 $是它的对手,说'这需要在字符串的末尾'。如果你希望你的整个字符串从头到尾匹配模式,这很好,但如果你想挑选多个数字,你应该去掉它们。

然而!
如果您将正则表达式保留为其当前形式的无锚点,则它现在将单独的数字与1,23,45的各个元素进行匹配。因此,为此,我们需要添加一个零宽度正向超前断言,并说:'确保在这个数字之后是空格或行尾“。您可以看到更改here。尾端(?=\s|$)是我们的前瞻断言:它不捕获任何东西,但只是确定标准或满足,在这种情况下,空格(\s)或(|)行结束($)。

但是:在类似的情况下,以前的正则表达式会在“1234,567”中匹配2个向前,给我们数字“234,567”,这会很糟糕。所以我们使用后面的断言,类似于我们在末尾的前瞻:(?<!^|\s),只有在字符串开头或数字前有空格时才匹配。该版本可以找到here,并且应该完全满足任何非十进制数字相关的需求。

+0

你的解释太棒了。我想我实际上可以把所有这些都包括在内。谢谢。 –

+0

很高兴听到它。不要忘记接受答案,这样它会泡到顶端,其他人可以阅读。 – BHustus

该问题是由于您在模式中使用了重复捕获组(,\d{3})*。 Python的正则表达式引擎将匹配您的号码的数千个和一组,但只有最后一次重复才会被捕获。我想你想要使用非捕获组。添加?:每个组括号开始(我也建议,在一般原则,使用原始的字符串,虽然你不必在当前的模式逃避问题):

numComma = re.compile(r'^(?:\d{1,3})(?:,\d{3})*$') 

由于没有组被捕获,re.findall将返回整个匹配的文本,我认为这是你想要的。您还可以使用re.findre.search并在返回的match对象上调用group()方法来获取整个匹配的文本。

+1

这是一个很好的解释,但建议的正则表达式匹配数字字符串“1234”,“123”,“1234,123”和许多其他变体 – mhawke

+0

@mhawke:是的,只是注意到了这一点。第一组的'*'也是错误的,虽然这不是导致问题所涉及的具体问题的原因,所以我没有注意到它。我已经在我的答案中解决了这个模式。 – Blckknght

+0

修正后的正则表达式现在可以很好地工作。一个非常小而迂腐的观点是,它不会接受负数......通过在开始处插入光标后加上“ - ?”可以很容易地解决这个问题。 – mhawke

尝试:

import re 
p = re.compile(ur'(?:(?<=^)|(?<=\s))((?:\d{1,3})(?:,\d{3})*)(?=\s|$)', re.DOTALL) 

test_str = """1,234 and 23,322 and 1,234,567 1,234,567,891 200 and 12 but 
not 1,23,1 or ,,1111, or anything else silly""" 

for m in re.findall(p, test_str): 
    print m 

,它的输出将是

  • 1,234
  • 23,322
  • 1,234,567
  • 1,234,567,891

你可以看到演示here

此正则表达式,将匹配任何有效的数量,并且将永远不会匹配无效号码:

(?<=^|\s)(?:(?:0|[1-9][0-9]{0,2}(?:,[0-9]{3})*))(?=\s|$)

https://regex101.com/r/dA4yB1/1