التعبير العادي لمطابقة التواريخ الصحيحة

StackOverflow https://stackoverflow.com/questions/51224

  •  09-06-2019
  •  | 
  •  

سؤال

أحاول كتابة تعبير عادي يتحقق من صحة التاريخ.يحتاج التعبير العادي إلى مطابقة ما يلي

  • ش/د/سسسس
  • ش ش/ي ي/س س س س
  • يمكن أن تبدأ الأشهر المكونة من رقم واحد بصفر بادئ (على سبيل المثال:03/12/2008)
  • يمكن أن تبدأ الأيام المكونة من رقم واحد بصفر بادئ (على سبيل المثال:02/03/2008)
  • لا يمكن تضمين 30 فبراير أو 31 فبراير (على سبيل المثال:31/02/2008)

حتى الآن لدي

^(([1-9]|1[012])[-/.]([1-9]|[12][0-9]|3[01])[-/.](19|20)\d\d)|((1[012]|0[1-9])(3[01]|2\d|1\d|0[1-9])(19|20)\d\d)|((1[012]|0[1-9])[-/.](3[01]|2\d|1\d|0[1-9])[-/.](19|20)\d\d)$

يتطابق هذا بشكل صحيح باستثناء أنه لا يزال يتضمن 2008/2/30 و31/2/2008.

هل لدى أي شخص اقتراح أفضل؟

يحرر: وجدت الاجابة على RegExLib

^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$

وهو يطابق جميع الأشهر الصالحة التي تتبع تنسيق MM/DD/YYYY.

شكرا جميعكم للمساعدة.

هل كانت مفيدة؟

المحلول

هذا ليس الاستخدام المناسب للتعبيرات العادية.سيكون من الأفضل لك أن تستخدم

[0-9]{2}/[0-9]{2}/[0-9]{4}

ثم التحقق من النطاقات في لغة ذات مستوى أعلى.

نصائح أخرى

إليك Reg ex الذي يطابق جميع التواريخ الصالحة بما في ذلك السنوات الكبيسة.التنسيقات المقبولة تنسيق mm/dd/yyyy أو mm-dd-yyyy أو mm.dd.yyyy

^(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

كياسة عازق احمد

لقد وصلت إلى هنا لأن عنوان هذا السؤال واسع النطاق وكنت أبحث عن تعبير عادي يمكنني استخدامه لمطابقة تنسيق تاريخ محدد (مثل OP).لكنني اكتشفت بعد ذلك، كما أبرزت العديد من الإجابات والتعليقات بشكل شامل، أن هناك العديد من المزالق التي تجعل بناء نمط فعال أمرًا صعبًا للغاية عند استخراج التواريخ الممزوجة ببيانات مصدر رديئة الجودة أو غير منظمة.

أثناء استكشافي للمشكلات، توصلت إلى نظام يمكّنك من إنشاء تعبير عادي من خلال ترتيب أربعة تعبيرات فرعية أبسط تتطابق مع المحدد، ونطاقات صالحة لحقول السنة والشهر واليوم بالترتيب تحتاج.

هؤلاء هم :-

المحددات

[^\w\d\r\n:] 

سيطابق هذا أي شيء ليس حرف كلمة أو حرف رقمي أو حرف إرجاع أو سطر جديد أو نقطتين.يجب أن تكون النقطتان موجودتين لمنع المطابقة في الأوقات التي تبدو مثل التواريخ (راجع بيانات الاختبار الخاصة بي)

يمكنك تحسين هذا الجزء من النموذج لتسريع المطابقة، ولكن هذا أساس جيد لاكتشاف معظم المحددات الصالحة.

لاحظ مع ذلك؛سوف يطابق سلسلة ذات محددات مختلطة مثل 2/12-73 والتي قد لا تكون في الواقع تاريخًا صالحًا.

قيم السنة

(\d{4}|\d{2})

يتطابق هذا مع مجموعة مكونة من رقمين أو 4 أرقام، وهذا مقبول في معظم الحالات، ولكن إذا كنت تتعامل مع بيانات من السنوات 0-999 أو ما بعد 9999، فيجب عليك أن تقرر كيفية التعامل مع ذلك لأنه في معظم الحالات 1، 3 أو > السنة المكونة من 4 أرقام هي قمامة.

قيم الشهر

(0?[1-9]|1[0-2])

يطابق أي رقم بين 1 و12 مع أو بدون صفر بادئ - ملاحظة:0 و 00 غير متطابقين.

قيم التاريخ

(0?[1-9]|[12]\d|30|31)

يطابق أي رقم بين 1 و31 مع أو بدون صفر بادئ - ملاحظة:0 و 00 غير متطابقين.

يتطابق هذا التعبير مع التواريخ المنسقة التاريخ والشهر والسنة

(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})

ولكنه سيطابق أيضًا بعضًا من السنة والشهر والتاريخ.ويجب أيضًا حجزها مع عوامل تشغيل الحدود للتأكد من تحديد سلسلة التاريخ بالكامل ومنع استخراج التواريخ الفرعية الصالحة من البيانات التي لم يتم تشكيلها بشكل جيد، على سبيل المثال.علامات بدون حدود 20/12/194 تتطابق مع 20/12/19 و101/12/1974 تتطابق مع 01/12/1974

قارن نتائج التعبير التالي بالتعبير أعلاه مع بيانات الاختبار في قسم الهراء (أدناه)

\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b

لا يوجد أي تحقق من الصحة في هذا التعبير العادي، لذا ستتم مطابقة تاريخ جيد الصياغة ولكنه غير صالح مثل 31/02/2001.هذه مشكلة تتعلق بجودة البيانات، وكما قال الآخرون، لا ينبغي أن يحتاج التعبير العادي الخاص بك إلى التحقق من صحة البيانات.

نظرًا لأنك (كمطور) لا يمكنك ضمان جودة البيانات المصدر التي تحتاجها لإجراء التحقق الإضافي والتعامل معه في التعليمات البرمجية الخاصة بك، إذا حاولت المطابقة و التحقق من صحة البيانات الموجودة في RegEx يصبح فوضويًا للغاية ويصبح من الصعب دعمه بدونه جداً وثائق موجزة.

القمامة في الداخل والقمامة في الخارج.

ومع ذلك، إذا كان لديك تنسيقات مختلطة حيث تختلف قيم التاريخ، وعليك استخراج أكبر قدر ممكن؛يمكنك الجمع بين تعبيرين معًا هكذا؛

يتطابق هذا التعبير (الكارثي) مع تواريخ DMY وYMD

(\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b)|(\b(0?[1-9]|1[0-2])[^\w\d\r\n:](0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](\d{4}|\d{2})\b)

ولكنك لن تتمكن من معرفة ما إذا كانت التواريخ مثل 6/9/1973 هي 6 سبتمبر أو 9 يونيو.أجد صعوبة في التفكير في سيناريو لا يتسبب فيه ذلك في حدوث مشكلة في مكان ما، فهي ممارسة سيئة ولا ينبغي عليك التعامل معها بهذه الطريقة - ابحث عن مالك البيانات واضربه بمطرقة الإدارة .

أخيرًا، إذا كنت تريد مطابقة سلسلة YYYYMMDD بدون محددات، فيمكنك إزالة بعض عدم اليقين وسيبدو التعبير هكذا

\b(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12]\d|30|31)\b

لكن لاحظ مرة أخرى أنه سيتطابق مع قيم جيدة الصياغة ولكنها غير صالحة مثل 20010231 (31 فبراير!) :)

بيانات الاختبار

في تجربة الحلول الواردة في هذا الموضوع، انتهى بي الأمر بمجموعة بيانات اختبار تتضمن مجموعة متنوعة من التواريخ الصالحة وغير الصالحة وبعض المواقف الصعبة التي قد ترغب أو لا ترغب في مطابقتها، على سبيل المثال.الأوقات التي يمكن أن تتطابق كتواريخ وتواريخ على أسطر متعددة.

آمل أن يكون هذا مفيدًا لشخص ما.

Valid Dates in various formats

Day, month, year
2/11/73
02/11/1973
2/1/73
02/01/73
31/1/1973
02/1/1973
31.1.2011
31-1-2001
29/2/1973
29/02/1976 
03/06/2010
12/6/90

month, day, year
02/24/1975 
06/19/66 
03.31.1991
2.29.2003
02-29-55
03-13-55
03-13-1955
12\24\1974
12\30\1974
1\31\1974
03/31/2001
01/21/2001
12/13/2001

Match both DMY and MDY
12/12/1978
6/6/78
06/6/1978
6/06/1978

using whitespace as a delimiter

13 11 2001
11 13 2001
11 13 01 
13 11 01
1 1 01
1 1 2001

Year Month Day order
76/02/02
1976/02/29
1976/2/13
76/09/31

YYYYMMDD sortable format
19741213
19750101

Valid dates before Epoch
12/1/10
12/01/660
12/01/00
12/01/0000

Valid date after 2038

01/01/2039
01/01/39

Valid date beyond the year 9999

01/01/10000

Dates with leading or trailing characters

12/31/21/
31/12/1921AD
31/12/1921.10:55
12/10/2016  8:26:00.39
wfuwdf12/11/74iuhwf
fwefew13/11/1974
01/12/1974vdwdfwe
01/01/99werwer
12321301/01/99

Times that look like dates

12:13:56
13:12:01
1:12:01PM
1:12:01 AM

Dates that runs across two lines

1/12/19
74

01/12/19
74/13/1946

31/12/20
08:13

Invalid, corrupted or nonsense dates

0/1/2001
1/0/2001
00/01/2100
01/0/2001
0101/2001
01/131/2001
31/31/2001
101/12/1974
56/56/56
00/00/0000
0/0/1999
12/01/0
12/10/-100
74/2/29
12/32/45
20/12/194

2/12-73

نسخة بيرل 5.10 قابلة للصيانة

/
  (?:
      (?<month> (?&mon_29)) [\/] (?<day>(?&day_29))
    | (?<month> (?&mon_30)) [\/] (?<day>(?&day_30))
    | (?<month> (?&mon_31)) [\/] (?<day>(?&day_31))
  )
  [\/]
  (?<year> [0-9]{4})

  (?(DEFINE)
    (?<mon_29> 0?2 )
    (?<mon_30> 0?[469]   | (11) )
    (?<mon_31> 0?[13578] | 1[02] )

    (?<day_29> 0?[1-9] | [1-2]?[0-9] )
    (?<day_30> 0?[1-9] | [1-2]?[0-9] | 30 )
    (?<day_31> 0?[1-9] | [1-2]?[0-9] | 3[01] )
  )
/x

يمكنك استرداد العناصر بالاسم في هذا الإصدار.

say "Month=$+{month} Day=$+{day} Year=$+{year}";

(لم يتم إجراء أي محاولة لتقييد قيم السنة.)

للتحكم بصلاحية التاريخ بالصيغة التالية :

YYYY/MM/DD أو YYYY-MM-DD

أنصحك باستخدام التعبير العادي التالي:

(((19|20)([2468][048]|[13579][26]|0[48])|2000)[/-]02[/-]29|((19|20)[0-9]{2}[/-](0[4678]|1[02])[/-](0[1-9]|[12][0-9]|30)|(19|20)[0-9]{2}[/-](0[1359]|11)[/-](0[1-9]|[12][0-9]|3[01])|(19|20)[0-9]{2}[/-]02[/-](0[1-9]|1[0-9]|2[0-8])))

اعواد الكبريت

2016-02-29 | 2012-04-30 | 2019/09/31

غير متطابقات

2016-02-30 | 2012-04-31 | 2019/09/35

يمكنك تخصيصه إذا كنت تريد السماح فقط بالفواصل "/" أو "-".يتحكم RegEx هذا بشكل صارم في صحة التاريخ ويتحقق من 28 و30 و31 يومًا من الأشهر، وحتى السنوات الكبيسة مع 29/02 شهرًا.

جربه، إنه يعمل بشكل جيد للغاية ويمنع الكود الخاص بك من الكثير من الأخطاء!

لعِلمِكَ :لقد قمت بإنشاء متغير لتاريخ SQL.ستجده هناك (ابحث عن اسمي): التعبير العادي للتحقق من صحة الطابع الزمني

ورحب ردود الفعل :)

يبدو أنك تفرط في توسيع التعبير العادي لهذا الغرض.ما سأفعله هو استخدام regex لمطابقة بعض تنسيقات التاريخ ثم استخدام وظيفة منفصلة للتحقق من صحة قيم حقول التاريخ المستخرجة على هذا النحو.

نسخة بيرل الموسعة

ملاحظة استخدام /x المعدل.

/^(
      (
        ( # 31 day months
            (0[13578])
          | ([13578])
          | (1[02])
        )
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
          | (3[01])
        )
      )
    | (
        ( # 30 day months
            (0[469])
          | ([469])
          | (11)
        )
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
          | (30)
        )
      )
    | ( # 29 day month (Feb)
        (2|02)
        [\/]
        (
            ([1-9])
          | ([0-2][0-9])
        )
      )
    )
    [\/]
    # year
    \d{4}$

  | ^\d{4}$ # year only
/x

إبداعي

^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$

إذا لم تنجح في تنفيذ هذه الاقتراحات المذكورة أعلاه، فأنا أستخدم هذا، لأنه يحصل على أي تاريخ، قمت بتشغيل هذا التعبير من خلال 50 رابطًا، وحصل على جميع التواريخ في كل صفحة.

^20\d\d-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(0[1-9]|[1-2][0-9]|3[01])$ 
    var dtRegex = new RegExp(/[1-9\-]{4}[0-9\-]{2}[0-9\-]{2}/);
    if(dtRegex.test(date) == true){
        var evalDate = date.split('-');
        if(evalDate[0] != '0000' && evalDate[1] != '00' && evalDate[2] != '00'){
            return true;
        }
    }

يتحقق هذا التعبير العادي من صحة التواريخ بين 01-01-2000 و12-31-2099 مع الفواصل المطابقة.

^(0[1-9]|1[012])([- /.])(0[1-9]|[12][0-9]|3[01])\2(19|20)\d\d$

لم يكن المقصود من Regex التحقق من صحة نطاقات الأرقام (يجب أن يكون هذا الرقم من 1 إلى 5 عندما يكون الرقم الذي يسبقه هو 2 والرقم الذي يسبقه أقل من 6).ما عليك سوى البحث عن نمط وضع الأرقام في التعبير العادي.إذا كنت بحاجة إلى التحقق من صحة خصائص التاريخ، فضعه في كائن تاريخ js/c#/vb، واستفسر عن الأرقام هناك.

أعلم أن هذا لا يجيب على سؤالك، ولكن لماذا لا تستخدم روتين معالجة التاريخ للتحقق مما إذا كان تاريخًا صالحًا؟حتى لو قمت بتعديل التعبير العادي بتأكيد نظرة سلبية مثل (؟!31/0?2) (على سبيل المثال، لا تتطابق مع 31/2 أو 31/02) ستظل تواجه مشكلة قبول 29 02 في السنوات غير الكبيسة وحول تنسيق تاريخ فاصل واحد.

المشكلة ليست سهلة إذا كنت تريد التحقق من صحة التاريخ حقًا، فتحقق من ذلك موضوع المنتدى.

للحصول على مثال أو طريقة أفضل، في C#، تحقق هذا الرابط

إذا كنت تستخدم منصة/لغة أخرى، فأخبرنا بذلك

نسخة بيرل 6

بعد استخدام هذا للتحقق من الإدخال، تتوفر القيم $/ أو بشكل فردي كما $<month>, $<day>, $<year>.(هذه مجرد بنية للوصول إلى القيم في $/ )

لم يتم إجراء أية محاولة للتحقق من السنة، أو أنها لا تتطابق مع يوم 29 فبراير في السنوات غير الكبيسة.

إذا كنت ستصر على القيام بذلك باستخدام تعبير عادي، فإنني أوصي بشيء مثل:

( (0?1|0?3| <...> |10|11|12) / (0?1| <...> |30|31) |
  0?2 / (0?1| <...> |28|29) ) 
/ (19|20)[0-9]{2}

هذا قد تجعل من الممكن القراءة والفهم.

نهج مختلف قليلاً قد يكون مفيدًا لك أو لا.

أنا في PHP.

المشروع المتعلق بهذا لن يكون له تاريخ قبل الأول من يناير 2008.لذا، آخذ "التاريخ" المُدخل وأستخدم strtotime().إذا كان الجواب >= 1199167200 فأنا عندي تاريخ مفيد لي.إذا تم إدخال شيء لا يبدو وكأنه تاريخ، فسيتم إرجاع -1.إذا تم إدخال قيمة فارغة، فإنها تقوم بإرجاع رقم تاريخ اليوم، لذا فأنت بحاجة إلى التحقق من الإدخال غير الفارغ أولاً.

هل يناسب وضعي، وربما وضعك أيضًا؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top