पार्सिंग स्ट्रिंग्स के एक गुच्छा को समझने और उन्हें किसी ऐसी चीज़ में बदलने की कला है जिसे हम समझ सकते हैं। आप रेगुलर एक्सप्रेशन का उपयोग कर सकते हैं, लेकिन वे हमेशा कार्य के लिए उपयुक्त नहीं होते हैं।
उदाहरण के लिए, यह सामान्य ज्ञान है कि नियमित अभिव्यक्तियों के साथ HTML को पार्स करना शायद एक अच्छा विचार नहीं है।
रूबी में हमारे पास नोकोगिरी है जो हमारे लिए यह काम कर सकती है, लेकिन आप अपना खुद का पार्सर बनाकर बहुत कुछ सीख सकते हैं। आइए शुरू करें!
रूबी के साथ पार्स करना
हमारे पार्सर का मूल StringScanner . है कक्षा।
इस वर्ग में एक स्ट्रिंग और एक स्थिति सूचक की एक प्रति है। पॉइंटर हमें कुछ टोकन की तलाश में स्ट्रिंग को पार करने की अनुमति देगा।
हम जिन विधियों का उपयोग करेंगे वे हैं:
- .पीक करें
- .स्कैन_तक
- .getch
एक और उपयोगी तरीका है .स्कैन (बिना तब तक)।
नोट :
यदि StringScanner आपके लिए उपलब्ध नहीं है तो require 'strscan'
adding जोड़ने का प्रयास करें
मैंने दस्तावेज़ीकरण के रूप में दो परीक्षण लिखे ताकि हम समझ सकें कि इस वर्ग को कैसे काम करना चाहिए:
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
इस वर्ग के बारे में ध्यान देने योग्य एक महत्वपूर्ण बात यह है कि कुछ विधियाँ स्थिति सूचक को आगे बढ़ाती हैं (प्राप्त करें, स्कैन करें ), जबकि अन्य नहीं करते (नज़र ) आप किसी भी समय अपने स्कैनर का निरीक्षण कर सकते हैं (.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> ">"
हम StringScanners के नज़र . का उपयोग करने जा रहे हैं यह देखने के लिए कि क्या हमारे इनपुट बफर पर अगला प्रतीक एक प्रारंभिक टैग है। अगर ऐसा है तो हम find_tag . को कॉल करेंगे और find_content हमारे पार्सर वर्ग पर विधियां:
def parse_element if @buffer.peek(1) == '<' @tags << find_tag last_tag.content = find_content end end
find_tag विधि होगी:
- शुरुआती टैग वर्ण 'उपभोग' करें
- क्लोजिंग सिंबल (">") मिलने तक स्कैन करें
- टैग नाम के साथ एक नया टैग ऑब्जेक्ट बनाएं और वापस करें
यहां कोड है, ध्यान दें कि हमें काट . कैसे करना है अंतिम चरित्र। ऐसा इसलिए है क्योंकि स्कैन_तक परिणामों में '>' शामिल है, और हम ऐसा नहीं चाहते हैं।
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। आप विस्तारित संस्करण के लिए 'नेस्टेड_टैग' शाखा भी देख सकते हैं जो किसी अन्य टैग के अंदर टैग से निपट सकता है।
निष्कर्ष
एक पार्सर लिखना एक दिलचस्प विषय है और यह कई बार काफी जटिल भी हो सकता है।
यदि आप खरोंच से अपना खुद का पार्सर नहीं बनाना चाहते हैं तो आप तथाकथित 'पार्सर जनरेटर' में से एक का उपयोग कर सकते हैं। रूबी में हमारे पास ट्रीटॉप और पार्सलेट है।