Может ли это регулярное выражение быть более эффективным? - PullRequest
0 голосов
/ 11 ноября 2009

Я изменил регулярное выражение, которое я получил здесь . Мне нужно было изменить его, потому что мне нужно было соответствовать следующим дополнительным критериям:

  1. Даты только с месяцем и годом
  2. полные даты в виде мм дд, гггг
  3. Даты только с годом
  4. Ввод с посторонней информацией (например, том 51, № 1, март 2008 г.)

Это то, что у меня есть до сих пор. Я сделал это с 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 мар»):

//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;
}

Спасибо!

EDIT Спасибо всем, кто нашел время, чтобы ответить. Вы, ребята, сделали то, на что я надеялся, указав на самые нелепые вещи в моей реализации. Я решил немного упростить основное регулярное выражение. Вот результат:

\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}

Это не беспокоит обнаружение недействительных дат, основанных на високосных годах или чем-то еще. @ Барт убедил меня, что это лучше всего сделать с нативным JS, чем с регулярным выражением. Спасибо @Tim также за указание на необходимость не захватывать скобки.

Если у кого-то есть дополнительные предложения о том, как мне улучшить это регулярное выражение, пожалуйста, откажитесь.

Ответы [ 2 ]

4 голосов
/ 11 ноября 2009

Должен сказать, что у меня проблемы с гроклингом этого монстра :) 1001 *

Две вещи, которые сразу бросаются в глаза:

  1. Было бы более эффективно использовать не захватывающие скобки (?:...), чем обычные скобки, если вы не планируете использовать их (под) совпадения позже.

  2. Если ваши скобки вложены в десять уровней, значит что-то не так. Это может сработать, но это просто. Или понять.

Я свяжусь с RegexMagic , возможно, есть лучший способ получить то, что вам нужно. Но так как никто не заставляет вас делать все, что вы хотите сделать в одном регулярном выражении, почему бы не разбить проблему на компоненты, используя одно, более простое регулярное выражение для каждого?

3 голосов
/ 11 ноября 2009

Как насчет этого:

#!/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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...