التعبير العادي:المطابقة بالإقصاء، دون النظر إلى الأمام – هل هذا ممكن؟

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

  •  19-08-2019
  •  | 
  •  

سؤال

في بعض صيغ التعبير العادي، لا يتم دعم التأكيدات ذات العرض الصفري [السالبة] (النظر للأمام/النظر للخلف).

وهذا يجعل من الصعب للغاية (من المستحيل؟) ذكر الاستبعاد.على سبيل المثال "كل سطر لا لديك "foo" عليها"، مثل هذا:

^((?!foo).)*$

هل يمكن تحقيق نفس الشيء دون استخدام النظرة الشاملة على الإطلاق (وضع التعقيد ومخاوف الأداء جانبًا في الوقت الحالي)؟

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

المحلول

تحديث: فشل "مع اثنين وما يليها قبل س س" كما أشارCiantic في التعليقات.


^(f(o[^o]|[^o])|[^f])*$

ملحوظة: من الأسهل بكثير إلغاء التطابق من جانب العميل بدلاً من استخدام التعبير العادي أعلاه.

يفترض التعبير العادي أن كل سطر ينتهي بحرف سطر جديد إذا لم يكن كذلك، فراجع التعبيرات العادية لـ C++ وgrep.

نماذج من البرامج في Perl وPython وC++ و grep جميعها تعطي نفس الناتج.

  • بيرل

    #!/usr/bin/perl -wn
    print if /^(f(o[^o]|[^o])|[^f])*$/;
    
  • بيثون

    #!/usr/bin/env python
    import fileinput, re, sys
    from itertools import ifilter
    
    re_not_foo = re.compile(r"^(f(o[^o]|[^o])|[^f])*$")
    for line in ifilter(re_not_foo.match, fileinput.input()):
        sys.stdout.write(line)
    
  • ج ++

    #include <iostream>
    #include <string>
    #include <boost/regex.hpp>
    
    int main()
    {
      boost::regex re("^(f(o([^o]|$)|([^o]|$))|[^f])*$");
      //NOTE: "|$"s are there due to `getline()` strips newline char
    
      std::string line;
      while (std::getline(std::cin, line)) 
        if (boost::regex_match(line, re))
          std::cout << line << std::endl;
    }
    
  • grep

    $ grep "^\(f\(o\([^o]\|$\)\|\([^o]\|$\)\)\|[^f]\)*$" in.txt
    

ملف العينة:

foo
'foo'
abdfoode
abdfode
abdfde
abcde
f

fo
foo
fooo
ofooa
ofo
ofoo

انتاج:

abdfode
abdfde
abcde
f

fo
ofo

نصائح أخرى

وجاء عبر هذا السؤال وأخذ حقيقة أن لم يكن هناك التعابير المنطقية كامل للعمل باعتباره تحديا شخصيا. أعتقد أنني قد تمكنت من خلق التعابير المنطقية أن <م> لا العمل لجميع المدخلات - شريطة يمكنك استخدام <لأ href = "https://www.regular-expressions.info/atomic.html" يختلط = "نوفولو noreferrer"> تجمع الذري / محددو الكمية غيور .

وبطبيعة الحال، أنا لست متأكدا مما اذا كان هناك <م> هي أي النكهات التي تسمح تجمع الذري ولكن ليس استكشاف الأجواء المحيطة، ولكن السؤال عما إذا كان من الممكن في التعابير المنطقية لدولة استثناء دون استكشاف الأجواء المحيطة، و< م> هو ممكنا من الناحية التقنية:

\A(?:$|[^f]++|f++(?:[^o]|$)|(?:f++o)*+(?:[^o]|$))*\Z

شرح:

\A                         #Start of string
(?:                        #Non-capturing group
    $                      #Consume end-of-line. We're not in foo-mode.
    |[^f]++                #Consume every non-'f'. We're not in foo-mode.
    |f++(?:[^o]|$)          #Enter foo-mode with an 'f'. Consume all 'f's, but only exit foo-mode if 'o' is not the next character. Thus, 'f' is valid but 'fo' is invalid.
    |(?:f++o)*+(?:[^o]|$)  #Enter foo-mode with an 'f'. Consume all 'f's, followed by a single 'o'. Repeat, since '(f+o)*' by itself cannot contain 'foo'. Only exit foo-mode if 'o' is not the next character following (f+o). Thus, 'fo' is valid but 'foo' is invalid.
)*                         #Repeat the non-capturing group
\Z                         #End of string. Note that this regex only works in flavours that can match $\Z

إذا، لأي سبب من الأسباب، يمكنك استخدام تجمع الذري ولكن ليس محددو الكمية غيور ولا استكشاف الأجواء المحيطة، يمكنك استخدام:

\A(?:$|(?>[^f]+)|(?>f+)(?:[^o]|$)|(?>(?:(?>f+)o)*)(?:[^o]|$))*\Z

وكما يشير آخرون، على الرغم من انها ربما أكثر واقعية لمجرد نفي مباراة من خلال وسائل أخرى.

ويمكنك عادة نبحث عن فو وعكس نتيجة مباراة التعبير العادي من رمز العميل.

لمثال بسيط، دعونا نقول لكم نريد للتحقق من أن سلسلة يحتوي فقط بعض الأحرف.

هل يمكن أن أكتب مثل هذا:

و^[A-Za-z0-9.$-]*$

وقبول نتيجة true صحيحا، أو مثل هذا:

و[^A-Za-z0-9.$-]

وقبول نتيجة false صحيحا.

وبطبيعة الحال، وهذا ليس دائما خيارا: في بعض الأحيان عليك أن تضع التعبير في ملف التكوين أو تمريرها إلى برنامج آخر، على سبيل المثال. ولكن الامر يستحق التذكر. مشكلة محددة، على سبيل المثال، كان التعبير <م> كثير بساطة إذا كان يمكنك استخدام نفي مثل هذا.

لقد عثرت على هذا السؤال أثناء البحث عن حل استبعاد regex الخاص بي، حيث أحاول استبعاد تسلسل داخل التعبير العادي الخاص بي.

رد فعلي الأولي على هذا الموقف: على سبيل المثال "كل سطر لا يحتوي على كلمة "foo" عليه" كان ببساطة استخدام الخيار -v invert بمعنى المطابقة في grep.

grep -v foo

يؤدي هذا إلى إرجاع كافة الأسطر الموجودة في ملف لا تتطابق مع "foo"

الأمر بسيط جدًا، لدي شعور قوي بأنني أخطأت في قراءة سؤالك....

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