2009-11-11 73 views
0

我修改了一個正則表達式,我得到here。我需要改變它,因爲我需要它符合以下附加標準:這個正則表達式可以更有效嗎?

  1. 日期只有月份和年份
  2. 全日期的形式毫米DD,YYYY與年僅
  3. 日期輸入無關信息(如卷51,第1號,2008年3月)

這是我迄今爲止。我用RegexBuddy做了這件事來幫助我解析邏輯,但它非常複雜,我不確定我是否有最有效的解決方案。

\b(?:((Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sept|Nov|Dec)(ember)?)|((((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?) 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sept|Nov|Dec)(ember)?) (0?[1-9]|([12]\d)|30))|(Feb(ruary)? (0?[1-9]|1\d|2[0-8]|(29(?=, ((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))))),)) ((1[6-9]|[2-9]\d)\d{2}))|((1[6-9]|[2-9]\d)\d{2})

有什麼可以做,以保持雙方原有的正則表達式和我的附加標準的功能?

這裏是我實現這個的代碼,如果它可以幫助你看到我想要做的。的parseDate函數的輸出被認爲是其形式爲「月日」的字符串日期(即,實施例4應該輸出「2008年3月」):

//generalized RegEx function 
function returnRegExMatch(ex,haystack) { 
    var needle = ex.exec(haystack); 
    if (needle) { return needle[0]; } 
} 

// date extraction (uses returnRegExMatch) 
function parseDate(date) { 
    //strip anything other than a valid date 
    var dateRe = /\b(?:((Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sept|Nov|Dec)(ember)?)|((((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?) 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sept|Nov|Dec)(ember)?) (0?[1-9]|([12]\d)|30))|(Feb(ruary)? (0?[1-9]|1\d|2[0-8]|(29(?=, ((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))))),)) ((1[6-9]|[2-9]\d)\d{2}))|((1[6-9]|[2-9]\d)\d{2})/; 
    date = returnRegExMatch(dateRe,date); 

    var yearRe = /[0-9][0-9][0-9][0-9]/; 
    var monthRe = /Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/; 
    var dayRe = /[0-9]?[0-9],/; 

    var year = returnRegExMatch(yearRe,date); 
    var month = returnRegExMatch(monthRe,date); 
    var day = parseInt(returnRegExMatch(dayRe,date),10); 

    var dateReturned = ""; 
    if (year) { dateReturned = year; } 
    if (month) { dateReturned = dateReturned + " " + month; } 
    if (month && day) { dateReturned = dateReturned + " " + day; } 

    return dateReturned; 
} 

謝謝!

編輯 感謝所有花時間回覆。你們做了我所希望的事,指出了我實施過程中最荒謬的事情。我決定簡化主要的正則表達式。結果如下:

\b(?:(?:Jan(?:uary)?|Feb(?:ruary)?|Ma(?:r(?:ch)?|y)|Apr(?:il)?|Ju(?:(?:ly?)|(?:ne?))|Aug(?:ust)?|Oct(?:ober)?|(?:Sept|Nov|Dec)(?:ember)?) (?:\d{1,2},)?)?\d{4} 

這不擔心檢測基於閏年或無論什麼無效日期。 @Bart讓我相信,這可能是最好的與本機JS比正則表達式。感謝@Tim也指出了對非捕獲括號的需求。

如果任何人有進一步的建議,我應該如何改進這個正則表達式請燒掉。

+0

你想讓它在你的例子匹配任何什麼。 4?它應該匹配「Vol。」中的所有內容嗎? – 2009-11-11 16:06:09

+0

哦,你真的需要匹配1600年的所有日期嗎?你是否真的需要在非閏年清除像2月30日或2月29日這樣的無效日期? – 2009-11-11 16:08:17

+0

我希望它與2008年3月的例子4相匹配。我不必匹配1600年的所有日期或清除無效日期,但我喜歡這種功能的想法。 – 2009-11-11 20:07:35

回答

4

我必須說,我有麻煩所著的Grokking這個怪物:)

有兩件事情是顯而易見:

  1. 這將是更有效地使用非捕獲括號(?:...)比普通括號,如果你不打算以後使用他們的(子)匹配。

  2. 如果您將括號嵌套到十個級別,則說明有問題。它可能工作,但這是一個維護。或理解。

我會RegexMagic檢查是否有可能是更好的方式來得到你所需要的。但是因爲沒有人強迫你在單個正則表達式中做所有你想做的事情,爲什麼不把它分解成組件,每個正則表達式使用一個簡單的正則表達式呢?

+0

+1 break'em up – mob 2009-11-11 16:13:59

3

怎麼是這樣的:

#!/usr/bin/js 

function getMonth(monthStr) { 
    var monthMap = new Array(); 
    monthMap['jan'] = monthMap['january'] = 1; 
    monthMap['feb'] = monthMap['february'] = 2; 
    monthMap['mar'] = monthMap['march']  = 3; 
    monthMap['apr'] = monthMap['april']  = 4; 
    monthMap['may']       = 5; 
    monthMap['jun'] = monthMap['june']  = 6; 
    monthMap['jul'] = monthMap['july']  = 7; 
    monthMap['aug'] = monthMap['august'] = 8; 
    monthMap['sep'] = monthMap['september'] = 9; 
    monthMap['oct'] = monthMap['october'] = 10; 
    monthMap['nov'] = monthMap['november'] = 11; 
    monthMap['dec'] = monthMap['december'] = 12; 
    return monthMap[monthStr.toLowerCase()]; 
} 

function isLeapYear(year) { 
    return year%400 == 0 || (year%100 != 0 && year%4 == 0); 
} 

function isPositiveNumber(str) { 
    return str.match(/^\d+$/); 
} 

function parseDate(date) { 
    var tokens = date.split(/,?\s+/); 

    var m = getMonth(tokens[0]); 
    var d = tokens[1]; 
    var y = tokens[2]; 

    if(!isPositiveNumber(d) || !m || !isPositiveNumber(y)) return false; 

    if(
     ((m==4 || m==6 || m==9 || m==11) && d <= 30) || 
     (m==2 && ((isLeapYear(y) && d <= 29) || d <= 28)) || 
     ((m==1 || m==3 || m==5 || m==7 || m==8 || m==10 || m==12) && d <= 31) 
    ) { 
     var dateObj = new Date(); 
     dateObj.setFullYear(y, m-1, d); 
     return dateObj; 
    } 

    return false; 
} 

var tests = new Array('January 31, 2009', 'Nov 31, 2009', 'Feb 29, 2001', 'Feb 29, 2000', 'Feb 29, 1900'); 

for(var i in tests) { 
    var date = parseDate(tests[i]); 
    print(date ? tests[i]+" is a valid date, parsed as: "+date : tests[i]+" invalid"); 
} 

輸出:

January 31, 2009 is a valid date, parsed as: Sat Jan 31 2009 20:31:33 GMT+0100 (CET) 
Nov 31, 2009 invalid 
Feb 29, 2001 invalid 
Feb 29, 2000 is a valid date, parsed as: Tue Feb 29 2000 20:31:33 GMT+0100 (CET) 
Feb 29, 1900 invalid 
+0

Bravo!可讀性的巨大提高。 如果您計算調試時需要花費的時間來理解這一點,那麼這樣做的效率也會大大提高。 (我認爲效率計算應該總是測量運行時間和程序員時間。) – 2009-11-11 19:40:11

+0

謝謝!只需要澄清一點:我希望parseDate函數返回一個不是Date對象的字符串。字符串格式有點奇怪,但這是代碼所在的應用程序需要的。我編輯了我的問題來反映這一點。 – 2009-11-11 20:46:10

+0

那麼,你需要稍微調整我的例子,或者更好:使用SimpleDateFormat類將日期格式化爲字符串。如果遇到問題,請告訴我。 – 2009-11-11 21:13:46

相關問題