PHP preg_replace处理各类年份格式(YYYY、YYYY-YYYY、YYYY - YYYY)
Alright, let's work through this regex conflict issue together. The main problem here is that your single-year pattern is catching parts of year ranges because it's being evaluated first. The fix is to prioritize matching the longer, more specific range patterns first, then target only standalone single years that aren't part of any range.
We'll use a sequential preg_replace workflow: first handle both types of year ranges, then match only single years that aren't part of a range. This avoids overlapping matches and ensures each format gets processed correctly.
Step 1: Define Targeted Regex Patterns
Here are the refined patterns, designed to avoid conflicts and accurately target each format:
1. Spaced Year Range (YYYY - YYYY)
'/(\b19[5-9]\d|20[01]\d|2020) - (\b19[5-9]\d|20[01]\d|2020)\b/'
- Matches valid 1950-2020 years on both sides of a spaced hyphen
- Uses
\bword boundaries to prevent partial matches (e.g., ignoring19500 - 2021)
2. Unspaced Year Range (YYYY-YYYY)
'/(\b19[5-9]\d|20[01]\d|2020)-(\b19[5-9]\d|20[01]\d|2020)\b/'
- Identical to the spaced range pattern, but targets hyphens without surrounding spaces
3. Standalone Single Year (YYYY)
'/(\b19[5-9]\d|20[01]\d|2020)\b(?!\s*-|\s*-\s*)/'
- Matches valid single years
- The negative lookahead
(?!\s*-|\s*-\s*)ensures the year isn't followed by a hyphen (with or without spaces), so it won't accidentally match the start of a range
Step 2: PHP Implementation Example
We'll chain the preg_replace calls to process ranges first, then single years. Adjust the replacement HTML to match your exact formatting needs (e.g., <br> tags, styled containers):
$input = "Sample text: 2014, 2000-2010, 1995 - 2005, and 1987 are all here."; // Process spaced year ranges first $output = preg_replace( '/(\b19[5-9]\d|20[01]\d|2020) - (\b19[5-9]\d|20[01]\d|2020)\b/', '<br><div class="year-range">Range: $1 - $2</div>', $input ); // Next process unspaced year ranges $output = preg_replace( '/(\b19[5-9]\d|20[01]\d|2020)-(\b19[5-9]\d|20[01]\d|2020)\b/', '<br><div class="year-range">Range: $1 - $2</div>', $output ); // Finally process standalone single years $output = preg_replace( '/(\b19[5-9]\d|20[01]\d|2020)\b(?!\s*-|\s*-\s*)/', '<br><div class="single-year">Year: $1</div>', $output ); echo $output;
Step 3: Expected Output
For the sample input above, the formatted output will be:
Sample text: <br><div class="single-year">Year: 2014</div>, <br><div class="year-range">Range: 2000 - 2010</div>, <br><div class="year-range">Range: 1995 - 2005</div>, and <br><div class="single-year">Year: 1987</div> are all here.
Key Fixes Explained
- Priority Order: By processing ranges first, we ensure the single-year pattern can't match parts of a range (like
2000in2000-2010). - Negative Lookahead: The single-year pattern skips any year that's about to be part of a range, eliminating false matches.
- Word Boundaries:
\bguarantees we only match full 4-digit years, not partial numbers embedded in other text.
内容的提问来源于stack exchange,提问作者Brian Bruman

