日期提取正则在线测试有效但Python regex库无效的原因
Hey there, let's break down why your Python code isn't extracting the dates as expected, and fix it up:
Why This Happens
There are two key issues at play here:
Anchor Restrictions (
^and$)
Your regex starts with^(matches the start of the string) and ends with$(matches the end). This means it will only match if the entire input string is exactly a date. While this works for validating standalone dates in regex testers, it limits flexibility—and even for your test case, the bigger problem comes from howre.findallbehaves.re.findallPrioritizes Capture Groups
By default,re.findallreturns content matched by capture groups (parentheses without the?:prefix) instead of the full matched text. Your regex uses multiple capture groups for date separators like(\/|-|\.), so it's returning those separators instead of the complete date string you want.
Fixes You Can Use
Option 1: Use re.finditer to Get Full Matches
Instead of re.findall, use re.finditer to iterate over match objects, then extract the full date string with group(0):
import re def extract_dates(text): date_regex = r'^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$' matches = re.finditer(date_regex, text) return [match.group(0) for match in matches] # Test it dates = extract_dates("02.02.2020") print(dates) # Output: ['02.02.2020']
Option 2: Modify the Regex to Remove Capture Groups
Change all your separator capture groups to non-capture groups (add ?: inside the parentheses) and remove ^/$ anchors if you want to extract dates from longer text. This way re.findall returns full date matches:
import re def extract_dates(text): # Updated regex: all separator groups are non-capturing, anchors removed for broader extraction date_regex = r'(?:(?:31(?:\/|-|\.)(?:0?[13578]|1[02])\1|(?:29|30)(?:\/|-|\.)(?:0?[13-9]|1[0-2])\2)(?:(?:1[6-9]|[2-9]\d)?\d{2})|(?:29(?:\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))|(?:0?[1-9]|1\d|2[0-8])(?:\/|-|\.)(?:0?[1-9]|1[0-2])\4(?:(?:1[6-9]|[2-9]\d)?\d{2}))' return re.findall(date_regex, text) # Test with multiple dates dates = extract_dates("Meeting on 02.02.2020 and 15/03/22") print(dates) # Output: ['02.02.2020', '15/03/22']
Option 3: Use re.fullmatch for Standalone Date Validation
If your goal is only to validate whether a string is a valid date (not extract from longer text), use re.fullmatch which checks the entire string and returns the full match object:
import re def is_valid_date(text): date_regex = r'^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$' match = re.fullmatch(date_regex, text) return match.group(0) if match else None # Test it date = is_valid_date("02.02.2020") print(date) # Output: '02.02.2020'
As for why online regex tools worked: most of them display the full matched string by default (not just capture groups) and test against the entire input string, so the ^/$ anchors behave as expected there.
内容的提问来源于stack exchange,提问作者Malintha

