2012-08-07 25 views
10

मुझे एक रेगेक्स मिला है जो एक टेम्पलेट सिस्टम के लिए मेल खाता है, जो दुर्भाग्य से कुछ मामूली-मामूली लुकअप पर अपाचे (यह विंडोज़ पर चल रहा है) को क्रैश करता है। मैंने इस मुद्दे पर शोध किया है और स्टैक आकार इत्यादि को अपनाने के लिए कुछ सुझाव दिए गए हैं, जिनमें से कोई भी काम नहीं कर रहा है और मुझे वास्तव में इस तरह के मुद्दों से निपटने में वाकई पसंद नहीं है क्योंकि यह आम तौर पर भविष्य में बग को धक्का देता है।PHP रेगेक्स क्रैशिंग अपाचे

वैसे भी रेगेक्स को बदलने के तरीके को कम करने की संभावना कम करने के बारे में कोई विचार है?

विचार सबसे निचले ब्लॉक को पकड़ने का विचार है (इस मामले में {block:test}This should be caught first!{/block:test}) जो मैं तब शुरू/समाप्ति टैग को str_replace कर दूंगा और जब तक कोई ब्लॉक नहीं छोड़ेगा तब तक पूरी चीज़ को regex के माध्यम से फिर से चलाएं।

Regex:

~(?P<opening>{(?P<inverse>[!])?block:(?P<name>[a-z0-9\s_-]+)})(?P<contents>(?:(?!{/?block:[0-9a-z-_]+}).)*)(?P<closing>{/block:\3})~ism 

नमूना टेम्पलेट:

<div class="f_sponsors s_banners"> 
    <div class="s_previous">&laquo;</div> 
    <div class="s_sponsors"> 
     <ul> 
      {block:sponsors} 
      <li> 
       <a href="{var:url}" target="_blank"> 
        <img src="image/160x126/{var:image}" alt="{var:name}" title="{var:name}" /> 
       </a> 
      {block:test}This should be caught first!{/block:test} 
      </li> 
      {/block:sponsors} 
     </ul> 
    </div> 
    <div class="s_next">&raquo;</div> 
</div> 

यह एक लंबे शॉट मुझे लगता है। :(

+4

अजीब के रूप में यह लग रहा है, मैं एक बार एक regex कि रखा था एक विशेष अपाचे उदाहरण और 'एस' (* ऊपरी मामला! *) ध्वज खाने से इसे ठीक किया गया। मैंने अनुमान लगाया कि यह एक अपरिवर्तित स्मृति रिसाव था या कुछ और अध्ययन प्रक्रिया से बचा जा सकता था। लंबे शॉट लेकिन कोशिश करने लायक है, मैं कहूंगा ... – DaveRandom

+1

@ डेव रैंडम, एक ही फिक्स के साथ, मुझे एक ही समस्या थी! चलिए देखते हैं कि यह ओपी –

+2

के लिए करता है एक और विचार यह होता है कि रेगेक्स में शाब्दिक '{}' वर्णों से बचने से मदद मिल सकती है। वे तकनीकी रूप से मेटा वर्ण हैं और पीसीआरई अनचाहे घुंघराले ब्रेसिज़ के बहुत क्षमा कर रहे हैं, लेकिन यदि आप उन्हें ठीक से बचते हैं तो यह कम काम कर सकता है। इसके अलावा नामित कैप्चर समूह का उपयोग क्यों करें और पीछे संदर्भों में नामों का उपयोग न करें? '/ block: \ 3' =>'/block :(? पी = नाम) '। ' 'के बाद यह आपके रेगेक्स के लिए विशेष रूप से सच है, जिस मामले में' '\ 2' होगा, \ \ 3' – DaveRandom

उत्तर

4

इस एक का प्रयास करें:

'~(?P<opening>\{(?P<inverse>[!])?block:(?P<name>[a-z0-9\s_-]+)\})(?P<contents>[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*)(?P<closing>\{/block:(?P=name)\})~i' 

या, पठनीय रूप में:

'~(?P<opening> 
    \{ 
    (?P<inverse>[!])? 
    block: 
    (?P<name>[a-z0-9\s_-]+) 
    \} 
) 
(?P<contents> 
    [^{]*(?:\{(?!/block:(?P=name)\})[^{]*)* 
) 
(?P<closing> 
    \{ 
    /block:(?P=name) 
    \} 
)~ix' 

सबसे महत्वपूर्ण हिस्सा (?P<contents>..) समूह में है:

[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)* 

बाहर शुरू, एकमात्र चरित्र जिसे हम रुचि रखते हैं वह उद्घाटन ब्रेस है, इसलिए हमके साथ किसी अन्य पात्र को स्लिप कर सकते हैं। केवल { देखने के बाद हम यह देखने के लिए जांचते हैं कि यह {/block} टैग की शुरुआत है या नहीं। यदि ऐसा नहीं है, तो हम आगे बढ़ते हैं और इसे उपभोग करते हैं और अगले के लिए स्कैनिंग शुरू करते हैं, और आवश्यकतानुसार दोहराते हैं।

रेगेक्सबड्डी का उपयोग करके, मैंने कर्सर को {block:sponsors} टैग और डिबगिंग की शुरुआत में प्रत्येक रेगेक्स का परीक्षण करके परीक्षण किया। तब मैंने एक असफल मैच को मजबूर करने के लिए समापन {/block:sponsors} टैग से समापन ब्रेस हटा दिया और इसे फिर से डीबग किया। आपके रेगेक्स ने सफल होने के लिए 940 कदम उठाए और असफल होने के लिए 2265 कदम उठाए। मेरा सफल होने के लिए 57 कदम और असफल होने के लिए 83 कदम उठाए।

एक तरफ नोट पर, मैंने s संशोधक को हटा दिया क्योंकि क्योंकि मैं डॉट (.), और m संशोधक का उपयोग नहीं कर रहा हूं क्योंकि इसकी आवश्यकता नहीं थी। मैंने @ डेवआरैंडम के उत्कृष्ट सुझाव के अनुसार \3 के बजाय नामित बैकरेफर (?P=name) का भी उपयोग किया। और मैं सभी ब्रेसिज़ ({ और }) से बच निकला क्योंकि मुझे इस तरह से पढ़ने में आसान लगता है।


संपादित करें: आप अंतरतम नामित ब्लॉक मैच के लिए चाहते हैं, इस से regex के मध्य भाग को बदलें: (

(?P<contents> 
    [^{]*(?:\{(?!/block:(?P=name)\})[^{]*)* 
) 

... इस के रूप में द्वारा सुझाए @ उसकी टिप्पणी में कोबी):

(?P<contents> 
    [^{]*(?:\{(?!/?block:[a-z0-9\s_-]+\})[^{]*)* 
) 

मूल रूप से, (?P<opening>...) समूह पहले खुलने वाला टैग हड़पने हैं यह देखा, तो (?P<contents>..) समूह अन्य टैग सहित - कुछ टैग्स का उपभोग करेगा - जब तक वे (?P<opening>...) समूह द्वारा प्राप्त किसी से मेल खाने के लिए समापन टैग नहीं थे। (फिर (?P<closing>...) समूह आगे बढ़ने और उपभोग कि।)

अब, (?P<contents>...) समूह (ध्यान दें शुरुआत में /?) किसी भी टैग, खोलने या बंद करने से मिलान करने, कोई बात नहीं क्या नाम है मना कर दिया। तो रेगेक्स शुरू में {block:sponsors} टैग से मेल खाना शुरू कर देता है, लेकिन जब यह {block:test} टैग से मुकाबला करता है, तो वह उस मैच को छोड़ देता है और एक ओपनिंग टैग खोजने के लिए वापस जाता है। यह {block:test} टैग पर फिर से शुरू होता है, इस बार जब यह {/block:test} समापन टैग पाता है तो सफलतापूर्वक मैच को पूरा कर लेता है।

यह इस तरह का वर्णन करने में अक्षम लगता है, लेकिन यह वास्तव में नहीं है। जिस चाल को मैंने पहले वर्णित किया था, गैर-ब्रेसिज़ को फिसलकर, इन झूठी शुरुआतओं के प्रभाव से डूब गया। जहां आप लगभग हर स्थिति में नकारात्मक दिख रहे थे, अब आप केवल एक ही कर रहे हैं जब आप { का सामना करते हैं।तुम भी, अधिकार परिमाणकों इस्तेमाल कर सकते हैं के रूप में के रूप में @godspeedlee सुझाव:

(?P<contents> 
    [^{]*+(?:\{(?!/?block:[a-z0-9\s_-]+\})[^{]*+)*+ 
) 

... क्योंकि आप जानते हैं कि यह कुछ भी है कि यह बाद में वापस देना होगा उपभोग कभी नहीं होगा। इससे चीजें थोड़ा तेज हो जाएंगी, लेकिन यह वास्तव में जरूरी नहीं है।

+0

मैंने प्रश्न को थोड़ा सा अद्यतन किया है क्योंकि यह केवल बाहरीतम ब्लॉक को पकड़ता है। हालांकि आपके अभी भी ~ sm भाग की आवश्यकता नहीं है, इसलिए मुझे लगता है कि यह सही रास्ते पर है! – Meep3D

+2

बहुत अच्छा! मैं * सोचता हूं * मैं समझता हूं कि ओपी क्या चाहता है ... मुझे लगता है कि "नेस्टेड ब्लॉक" से उनका मतलब है कि वे किसी भी नाम के घोंसले वाले ब्लॉक हैं, न कि केवल उसी नाम के, जो इसे बदले में बदल दिया जाता है। इसलिए '{1} {2} {/ 2} {/ 1}' '1' से पहले' 2' को कैप्चर करना चाहिए। यदि ऐसा है, तो आप आसानी से मध्य भाग को बदल सकते हैं, '[^ {] * (?: \ {(?!/Block :(? पी = नाम) \}) [^ {] *) * 'से '[^ {] * (?: \ {(?! /? ब्लॉक: [a-z0-9 \ s _-] + \}) [^ {] *) *' - http://regexr.com?31qsf – Kobi

+0

एचटीएमएल ] *)?> (? :(?!)।) * # s' क्रैश हो गया जबकि' # # s' काम किया। हालांकि 'यू' अपरिवर्तनीय संशोधक को जोड़ने से भी दुर्घटना को रोका गया; मुझे यकीन नहीं है कि जैसा कि मैंने सोचा था कि अधिक बैकट्रैकिंग और अक्षमता का कारण बनता है - यदि नहीं, तो मैंने अभी भी लिखा होगा '# # Us' और साथ किया जाना चाहिए यह ... – Jake

4

आप atomic group: (?>...) या possessive quantifiers: ?+ *+ ++.. का उपयोग दबाने/बैक ट्रैकिंग सीमित करने और unrolling loop तकनीक से मिलान तेजी लाने के लिए कर सकता है मेरे समाधान:।।

\{block:(\w++)\}([^<{]++(?:(?!\{\/?block:\1\b)[<{][^<{]*+)*+)\{/block:\1\}

मैं http://regexr.com?31p03 से परीक्षण किया है

मैच {block:sponsors}...{/block:sponsors}:
\{block:(sponsors)\}([^<{]++(?:(?!\{\/?block:\1\b)[<{][^<{]*+)*+)\{/block:\1\}
http://regexr.com?31rb3

मैच {block:test}...{/block:test}:
\{block:(test)\}([^<{]++(?:(?!\{\/?block:\1\b)[<{][^<{]*+)*+)\{/block:\1\}
http://regexr.com?31rb6

अन्य समाधान: config.h से पाठ की प्रतिलिपि निम्नलिखित
/* #undef NO_RECURSE */

:
PCRE स्रोत कोड में, आप config.h से टिप्पणी को हटा सकते हैं :
मिलान करते समय पीसीआरई बैकट्रैकिंग को संभालने के लिए रिकर्सिव फ़ंक्शन कॉल का उपयोग करता है। यह कभी-कभी सिस्टम पर एक समस्या हो सकती है जिसमें सीमित आकार के ढेर होते हैं। कोई संस्करण प्राप्त करने के लिए NO_RECURSE को परिभाषित करें जो मिलान() फ़ंक्शन में रिकर्सन का उपयोग नहीं करता है; इसके बजाय यह ढेर से स्मृति प्राप्त करने के लिए pcre_recurse_malloc() का उपयोग करके भाप द्वारा अपना स्वयं का ढेर बनाता है।

या आप pcre.backtrack_limit और pcre.recursion_limitphp.ini से बदल सकता है (http://www.php.net/manual/en/pcre.configuration।php)

+0

हालांकि जब आप किसी ब्लॉक के भीतर एक ब्लॉक डालते हैं तो यह केवल सबसे निचले ब्लॉक की बजाय बाहरीतम ब्लॉक को पकड़ता है। मैंने कुछ हद तक सवाल अपडेट कर लिया है! – Meep3D

4

क्या समाधान को एक नियमित अभिव्यक्ति होना चाहिए? {/block: (जो एक साधारण स्ट्रिंग सर्च या रेगेक्स हो सकता है) की पहली घटना को देखने के लिए एक और अधिक कुशल दृष्टिकोण हो सकता है और फिर उस बिंदु से पीछे की तरफ से अपने मिलान करने वाले टैग को ढूंढने के लिए खोज करें, स्पैन को उचित रूप से बदलें और तब तक दोहराएं जब तक कि वहां न हों कोई और ब्लॉक नहीं यदि हर बार आप पहले टेम्पलेट के शीर्ष से शुरू होने वाले बंद टैग की तलाश करते हैं तो यह आपको सबसे गहरा घोंसला वाला ब्लॉक देगा।

दर्पण छवि एल्गोरिथ्म बस के रूप में अच्छी तरह से काम करेगा - पिछले खुलने वाला टैग के लिए देखो और फिर इसी अंतिम टैग के लिए वहाँ से आगे खोज:

<?php 

$template = //... 

while(true) { 
    $last_open_tag = strrpos($template, '{block:'); 
    $last_inverted_tag = strrpos($template, '{!block:'); 
    // $block_start is the index of the '{' of the last opening block tag in the 
    // template, or false if there are no more block tags left 
    $block_start = max($last_open_tag, $last_inverted_tag); 
    if($block_start === false) { 
    // all done 
    break; 
    } else { 
    // extract the block name (the foo in {block:foo}) - from the character 
    // after the next : to the character before the next }, inclusive 
    $block_name_start = strpos($template, ':', $block_start) + 1; 
    $block_name = substr($template, $block_name_start, 
     strcspn($template, '}', $block_name_start)); 

    // we now have the start tag and the block name, next find the end tag. 
    // $block_end is the index of the '{' of the next closing block tag after 
    // $block_start. If this doesn't match the opening tag something is wrong. 
    $block_end = strpos($template, '{/block:', $block_start); 
    if(strpos($template, $block_name.'}', $block_end + 8) !== $block_end + 8) { 
     // non-matching tag 
     print("Non-matching tag found\n"); 
     break; 
    } else { 
     // now we have found the innermost block 
     // - its start tag begins at $block_start 
     // - its content begins at 
     // (strpos($template, '}', $block_start) + 1) 
     // - its content ends at $block_end 
     // - its end tag ends at ($block_end + strlen($block_name) + 9) 
     // [9 being the length of '{/block:' plus '}'] 
     // - the start tag was inverted iff $block_start === $last_inverted_tag 
     $template = // do whatever you need to do to replace the template 
    } 
    } 
} 

echo $template; 
संबंधित मुद्दे