2011-08-13 10 views
9

एक कुशलता से किसी भी नियमित अभिव्यक्ति के खिलाफ एक इनपुट स्ट्रिंग से मेल खाता है?एक बार में कई नियमित अभिव्यक्तियों के खिलाफ इनपुट स्ट्रिंग को कुशलता से कैसे मिलान करें?

एक परिदृश्य जहां यह उपयोगी हो सकता है आरईएसटी वेब सेवाओं के साथ है।

  • /user/with-id/{userId}
  • /user/with-id/{userId}/profile
  • /user/with-id/{userId}/preferences
  • : मान लेते हैं कि मैं एक बाकी वेब सेवा के सार्वजनिक इंटरफेस के लिए URL प्रतिमानों की संख्या के साथ आए हैं चलो /users
  • /users/who-signed-up-on/{date}
  • /users/who-signed-up-between/{fromDate}/and/{toDate}
  • ...

जहां {…} (नियमित अभिव्यक्ति पर कब्जा समूह) के प्लेसहोल्डर नाम हैं।

ध्यान दें: यह सवाल इसके बाद के संस्करण REST इंटरफ़ेस अच्छी तरह से डिजाइन है या नहीं के बारे में नहीं है। (यह शायद नहीं है, लेकिन है कि इस सवाल का के संदर्भ में कोई फर्क नहीं करना चाहिए।)

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

अब वेब सेवा को एक अनुरोध प्राप्त होता है। बेशक, कोई अनुक्रमिक रूप से अनुरोधित यूआरआई से एक यूआरएल पैटर्न के खिलाफ मिलान कर सकता है, फिर अगले के खिलाफ, और इसी तरह; लेकिन संभवतः उन बड़ी संख्या में पैटर्न के लिए अच्छी तरह से स्केल नहीं किया जाएगा जिन्हें जांचना चाहिए।

क्या इसके लिए कोई कुशल एल्गोरिदम हैं?

इनपुट:

  • एक इनपुट स्ट्रिंग
  • "परस्पर अनन्य" नियमित अभिव्यक्ति (यानी कोई इनपुट स्ट्रिंग एक से अधिक एक्सप्रेशन से मेल कर सकते हैं।)

आउटपुट का एक सेट :

  • नियमित अभिव्यक्ति (यदि कोई है) कि इनपुट स्ट्रिंग के खिलाफ मिलान किया गया है।

उत्तर

10

Aho-Corasick algorithm पैटर्न के एक सेट (वास्तव में कीवर्ड) के खिलाफ एक इनपुट स्ट्रिंग से मेल खाने के लिए एक बहुत तेज़ एल्गोरिदम है, जो प्रीपेक्सेस्ड और ट्राई में संगठित होते हैं, मिलान को गति देने के लिए।

रेगेक्स पैटर्न (यानी http://code.google.com/p/esmre/ सिर्फ एक नाम देने के लिए) के लिए एल्गोरिदम की भिन्नताएं हैं जो शायद एक नजर के लायक हैं।

या, आप यूआरएल को टुकड़ों में विभाजित कर सकते हैं, उन्हें एक पेड़ में व्यवस्थित कर सकते हैं, फिर यूआरएल को मिलान करने के लिए विभाजित कर सकते हैं और एक समय में पेड़ को एक टुकड़ा चल सकते हैं। {UserId} को वाइल्डकार्ड माना जा सकता है, या कुछ विशिष्ट प्रारूप (यानी एक int होना) से मेल खाता है।

जब आप एक पत्ता तक पहुँचते हैं, क्या आप जानते हैं कि कौन सा URL आप

मिलान किया
+0

क्या किसी भी मौके से इसका सी ++ कार्यान्वयन है? – nurettin

+0

http://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_string_matching_algorithm में कुछ कार्यान्वयन के लिंक हैं। मुझे याद है http://sourceforge.net/projects/snort/ को सी में कहीं भी कार्यान्वित किया गया था, लेकिन यह कई साल पहले था, मैं गलत हो सकता था। –

+0

मुझे पता चला कि Google की री 2 लाइब्रेरी अहो-कोरासिक एल्गोरिदम – nurettin

1

नामित अभिव्यक्तियों और OR ऑपरेटर का उपयोग करें, यानी "(?P<re1>...)|(?P<re2>...)|..."।

+2

क्या यह वही प्रदर्शन नहीं होगा जैसा कि परीक्षण re1, re2 .. अनुक्रमिक रूप से और पहले मैच में रोक रहा है? –

+0

@ एंडर्स: जरूरी नहीं। यदि matcher को बेवकूफ ढंग से कार्यान्वित किया जाता है, हाँ, लेकिन इस तरह के मिलान कुशलतापूर्वक करने से लंबे समय तक प्रभावी समाधान हुए हैं। मेरे उत्तर पुनः lexer जेनरेटर देखें। –

+0

@Ira यकीन है, लेकिन इस सुझाए गए उत्तर में उस प्रकार का लेक्सर शामिल नहीं है, केवल सभी रेगेक्स को एक ही रेगेक्स में एकाधिक नामांकित समूह के साथ संयोजित करता है यदि मैं सही ढंग से उत्तर समझता हूं (और .NET के regexes कैसे काम करते हैं) –

3

यदि यूआरएल संरचना में पदानुक्रम है, तो इसे प्रदर्शन को अधिकतम करने के लिए उपयोग किया जाना चाहिए। केवल एक यूआरएल जो उपयोगकर्ता/उपयोगकर्ता/से शुरू होता है, पहले तीनों में से किसी एक से मेल खा सकता है।

मैं यूआरएल पदानुक्रम से संबंधित पेड़ में मिलान करने के लिए पदानुक्रम संग्रहित करने का सुझाव देता हूं, जहां प्रत्येक नोड पदानुक्रम में एक स्तर से मेल खाता है। यूआरएल से मेल खाने के लिए, पेड़ की सभी जड़ों के खिलाफ यूआरएल का परीक्षण करें जहां "उपयोगकर्ता" और "उपयोगकर्ता" के लिए रेगेक्स के साथ केवल नोड्स हैं। यूआरएल मिलान करना: उन नोड्स के बच्चों के खिलाफ परीक्षण किया जाता है जब तक कि एक पत्ता नोड में एक मैच नहीं मिलता है। जड़ से पत्ते तक नोड्स की सूची के रूप में एक सफल मैच लौटाया जा सकता है। {User-id} जैसी संपत्ति मान वाले नामांकित समूह सफल मिलान के नोड्स से प्राप्त किए जा सकते हैं।

1

सबसे पहले हालांकि मैं इस प्रक्रिया के लिए कोई अच्छा अनुकूलन नहीं देख सका।

हालांकि, यदि आपके पास वास्तव में बड़ी संख्या में रेगेक्स हैं तो आप उन्हें विभाजित करना चाहेंगे (मुझे यकीन नहीं है कि यह तकनीकी रूप से विभाजन कर रहा है)।

क्या मैं आपको बता क्या करना है:

मान लीजिए आप 20 संभव यूआरएल कि user के साथ शुरू है:

/user/with-id/X 
/user/with-id/X/preferences # instead of preferences, you could have another 10 possibilities like /friends, /history, etc 

उसके बाद, आप भी 20 संभव यूआरएल users के साथ शुरू की है:

/users/who-signed-up-on 
/users/who-signed-up-on-between  #others: /registered-for, /i-might-like, etc 

और सूची उपयोगकर्ताओं के बजाय /products, /companies आदि के लिए चलती है।

इस मामले में आप क्या कर सकते हैं "बहु-स्तर" मिलान का उपयोग कर रहा है।

सबसे पहले, स्ट्रिंग की शुरुआत से मेल खाते हैं। आप /products, /companies, /users, एक समय में और शेष स्ट्रिंग को अनदेखा कर रहे हैं। इस तरह, आपको सभी 100 संभावनाओं का परीक्षण करने की आवश्यकता नहीं है।

आपको पता है कि यूआरएल /users से शुरू होता है, तो आप केवल संभावित यूआरएल से मेल खाते हैं जो उपयोगकर्ताओं के साथ शुरू होते हैं।

इस तरह, आप बहुत सारे अनियंत्रित मैचों को कम कर देंगे। आप सभी /procucts संभावनाओं के लिए स्ट्रिंग से मेल नहीं खाएंगे।

4

एक इनपुट स्ट्रीम के खिलाफ कई नियमित अभिव्यक्ति मिलान के लिए मानक समाधान है एक lexer-generator ऐसे फ्लेक्स के रूप में (के बहुत देखते हैं इन आते, आम तौर पर प्रत्येक के लिए कई प्रोग्रामिंग लैंगेज)।

ये उपकरण "टोकन" से जुड़े नियमित अभिव्यक्तियों का एक सेट लेते हैं (टोकन के बारे में सोचें जो कि नियमित अभिव्यक्ति मिलान के लिए सिर्फ नाम हैं) और एक ही समय में सभी regexes से मेल खाने के लिए कुशल परिमित-राज्य automata उत्पन्न करता है। यह इनपुट स्ट्रीम के आकार में बहुत ही कम स्थिरता के साथ रैखिक समय है; इससे "तेज" पूछना मुश्किल है। आप इसे एक चरित्र धारा खिलाते हैं, और यह रेगेक्स के टोकन नाम को उत्सर्जित करता है जो "सर्वश्रेष्ठ" से मेल खाता है (यह उस मामले को संभालता है जहां दो रेगेक्स एक ही स्ट्रिंग से मेल खाते हैं; इसकी परिभाषा के लिए लेक्सर जनरेटर देखें), और धारा को आगे बढ़ाता है क्या पहचाना गया था द्वारा। तो आप टोकन की एक श्रृंखला के लिए इनपुट स्ट्रीम से मेल खाने के लिए इसे बार-बार लागू कर सकते हैं।

विभिन्न लेक्सर जेनरेटर आपको अलग-अलग तरीकों से मान्यता प्राप्त धारा के विभिन्न बिट्स को कैप्चर करने की अनुमति देंगे, ताकि आप टोकन को पहचानने के बाद, उस हिस्से को चुन सकें जिसके बारे में आप परवाह करते हैं (उदाहरण के लिए, कोट्स में एक शाब्दिक स्ट्रिंग के लिए, आप केवल स्ट्रिंग सामग्री के बारे में परवाह करें, उद्धरण नहीं)।

+0

+1 का उपयोग कर रेगेक्स से मेल खा सकती है, भले ही मेरे पास इस समाधान के साथ एक समस्या हो, यानी कि सभी पैटर्न को * बाहरी * टूल द्वारा पूर्व-संसाधित किया जाना चाहिए, जो किसी के स्वयं को बदल सकता है कार्यक्रम की कॉन्फ़िगरेशन एक और जटिल प्रक्रिया है। निस्संदेह किसी एक कार्यक्रम में 'लेक्स'/'फ्लेक्स' आदि के व्यवहार को दोहरा सकता है, लेकिन यह थोड़ा अधिक हो सकता है। – stakx

+0

@stakx: यदि आप उच्च दक्षता चाहते हैं, तो एक लेक्सर जनरेटर उत्तर है। यदि आप पुनर्निर्माण मूल्य नहीं चाहते हैं, तो, आपको इसे स्वयं दोहराना होगा (या लाइब्रेरी के साथ एक भाषा चुनें जिसमें इसे बनाया गया है, मेरा मानना ​​है कि जावा का रेगेक्स ऐसा करता है)। रीस्टफुल सेवाओं के आपके उदाहरण के उदाहरण के लिए, मुझे नहीं लगता कि बाहरी लेक्सर के साथ बिल्ड जटिलताओं में कोई वास्तविक कठिनाई है: यह आपकी निर्माण प्रक्रिया में केवल एक और कदम जोड़ता है। –

संबंधित मुद्दे