पार्सिंग स्ट्रिंग्स के एक समूह को समझने और उन्हें किसी ऐसी चीज़ में बदलने की कला है जिसे हम समझ सकें। आप रेगुलर एक्सप्रेशन्स का उपयोग कर सकते हैं, लेकिन वे हमेशा इस काम के लिए उपयुक्त नहीं होते।
उदाहरण के लिए, यह सामान्य ज्ञान है कि HTML को रेगुलर एक्सप्रेशन्स से पार्स करना शायद एक अच्छा विचार नहीं है।
रूबी में हमारे पास नोकोगिरी है जो यह काम हमारे लिए कर सकता है, लेकिन आप अपना खुद का पार्सर बनाकर बहुत कुछ सीख सकते हैं। आइए शुरू करें!
रूबी के साथ पार्सिंग
हमारे पार्सर का मूल StringScanner क्लास है।
यह क्लास एक स्ट्रिंग की कॉपी और एक पोजीशन पॉइंटर रखती है। पॉइंटर हमें कुछ टोकन्स की खोज में स्ट्रिंग को पार करने की अनुमति देगा।
हम जिन विधियों का उपयोग करेंगे वे हैं:
.peek.scan_until.getch
एक और उपयोगी विधि है .scan (बिना until के)।
नोट:
यदि StringScanner आपके लिए उपलब्ध नहीं है तो require 'strscan' जोड़ने का प्रयास करें।
मैंने दो टेस्ट डॉक्यूमेंटेशन के रूप में लिखे हैं ताकि हम समझ सकें कि यह क्लास कैसे काम करती है:
describe StringScanner do let (:buff) { StringScanner.new "testing" } it "can peek one step ahead" do expect(buff.peek 1).to eq "t" end it "can read one char and return it" do expect(buff.getch).to eq "t" expect(buff.getch).to eq "e" end end
इस क्लास के बारे में ध्यान देने वाली एक महत्वपूर्ण बात यह है कि कुछ विधियाँ पोजीशन पॉइंटर को आगे बढ़ाती हैं (getch, scan), जबकि अन्य नहीं (peek)। किसी भी बिंदु पर आप अपने स्कैनर का निरीक्षण कर सकते हैं (.inspect या p का उपयोग करके) यह देखने के लिए कि यह कहां है।
पार्सर क्लास
पार्सर क्लास वह जगह है जहां अधिकांश काम होता है, हम इसे उस टेक्स्ट के स्निपेट के साथ इनिशियलाइज़ करेंगे जिसे हम पार्स करना चाहते हैं और यह उसके लिए एक StringScanner बनाएगा और पार्स विधि को कॉल करेगा:
def initialize(str) @buffer = StringScanner.new(str) @tags = [] parse end
टेस्ट में हम इसे इस तरह परिभाषित करते हैं:
let(:parser) { Parser.new "<body>testing</body> <title>parsing with ruby</title>" }
हम थोड़ी देर में देखेंगे कि यह क्लास अपना काम कैसे करती है, लेकिन पहले आइए हमारे प्रोग्राम के अंतिम भाग पर एक नज़र डालते हैं।
टैग क्लास
यह क्लास बहुत सरल है, यह मुख्य रूप से पार्सिंग परिणामों के लिए एक कंटेनर और डेटा क्लास के रूप में कार्य करती है।
class Tag attr_reader :name attr_accessor :content def initialize(name) @name = name end end
आइए पार्स करें!
किसी चीज़ को पार्स करने के लिए हमें अपने इनपुट टेक्स्ट को देखकर पैटर्न ढूंढने होंगे। उदाहरण के लिए, हम जानते हैं कि HTML कोड का निम्नलिखित रूप होता है:
<tag>contents</tag>
यहां स्पष्ट रूप से दो अलग-अलग घटक हैं जिन्हें हम पहचान सकते हैं, टैग नाम और टैग्स के अंदर का टेक्स्ट। यदि हम BNF नोटेशन का उपयोग करके एक औपचारिक व्याकरण को परिभाषित करते तो यह कुछ इस तरह दिखता:
tag = <opening_tag> <contents> <closing_tag> opening_tag = "<" <tag_name> ">" closing_tag = "</" <tag_name> ">"
हम StringScanner के peek का उपयोग यह देखने के लिए करेंगे कि क्या हमारे इनपुट बफर पर अगला सिंबल एक ओपनिंग टैग है। यदि ऐसा है तो हम अपने पार्सर क्लास पर find_tag और find_content विधियों को कॉल करेंगे:
def parse_element if @buffer.peek(1) == '<' @tags << find_tag last_tag.content = find_content end end
find_tag विधि होगी:
- ओपनिंग टैग कैरेक्टर को 'कंज्यूम' करना
- क्लोजिंग सिंबल (">") मिलने तक स्कैन करना
- टैग नाम के साथ एक नया Tag ऑब्जेक्ट बनाना और वापस करना
यहां कोड है, ध्यान दें कि हमें अंतिम कैरेक्टर को चॉप करना होगा। ऐसा इसलिए है क्योंकि scan_until परिणामों में '>' को शामिल करता है, और हम वह नहीं चाहते।
def find_tag @buffer.getch tag = @buffer.scan_until />/ Tag.new(tag.chop) end
अगला कदम टैग के अंदर की सामग्री को ढूंढना है, यह बहुत कठिन नहीं होना चाहिए क्योंकि scan_until विधि पोजीशन पॉइंटर को सही स्थान पर आगे बढ़ाती है। हम क्लोजिंग टैग को ढूंढने और टैग सामग्री वापस करने के लिए फिर से scan_until का उपयोग करने जा रहे हैं।

def find_content tag = last_tag.name content = @buffer.scan_until /<\/#{tag}>/ content.sub("</#{tag}>", "") end
अब:
हमें बस parse_element को एक लूप में कॉल करना है जब तक कि हम अपने इनपुट बफर पर और टैग न ढूंढ सकें।
def parse until @buffer.eos? skip_spaces parse_element end end
आप पूरा कोड यहां पा सकते हैं: https://github.com/matugm/simple-parser। आप नेस्टेड टैग्स को संभालने वाले विस्तारित संस्करण के लिए 'nested_tags' ब्रांच भी देख सकते हैं।
निष्कर्ष
पार्सर लिखना एक दिलचस्प विषय है और यह कई बार काफी जटिल हो सकता है।
यदि आप स्क्रैच से अपना खुद का पार्सर नहीं बनाना चाहते हैं तो आप तथाकथित 'पार्सर जनरेटर्स' में से एक का उपयोग कर सकते हैं। रूबी में हमारे पास ट्रीटॉप और पार्सलेट हैं।