Computer >> कंप्यूटर >  >> प्रोग्रामिंग >> Ruby

रूबी के साथ एक पार्सर कैसे बनाएं

पार्सिंग स्ट्रिंग्स के एक गुच्छा को समझने और उन्हें किसी ऐसी चीज़ में बदलने की कला है जिसे हम समझ सकते हैं। आप रेगुलर एक्सप्रेशन का उपयोग कर सकते हैं, लेकिन वे हमेशा कार्य के लिए उपयुक्त नहीं होते हैं।

उदाहरण के लिए, यह सामान्य ज्ञान है कि नियमित अभिव्यक्तियों के साथ 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 . पर कॉल करें जब तक हम अपने इनपुट बफ़र पर अधिक टैग नहीं ढूंढ पाते, तब तक लूप पर।

@buffer.eos तक
def parse
  until @buffer.eos?
    skip_spaces
    parse_element
  end
end

आप पूरा कोड यहां देख सकते हैं: https://github.com/matugm/simple-parser। आप विस्तारित संस्करण के लिए 'नेस्टेड_टैग' शाखा भी देख सकते हैं जो किसी अन्य टैग के अंदर टैग से निपट सकता है।

निष्कर्ष

एक पार्सर लिखना एक दिलचस्प विषय है और यह कई बार काफी जटिल भी हो सकता है।

यदि आप खरोंच से अपना खुद का पार्सर नहीं बनाना चाहते हैं तो आप तथाकथित 'पार्सर जनरेटर' में से एक का उपयोग कर सकते हैं। रूबी में हमारे पास ट्रीटॉप और पार्सलेट है।


  1. रूबी मानचित्र विधि का उपयोग कैसे करें (उदाहरण के साथ)

    मैप एक रूबी विधि है जिसका उपयोग आप ऐरे, हैश और रेंज के साथ कर सकते हैं। मानचित्र का मुख्य उपयोग डेटा को ट्रांसफ़ॉर्म करना है। उदाहरण के लिए : स्ट्रिंग्स की एक सरणी को देखते हुए, आप प्रत्येक स्ट्रिंग पर जा सकते हैं और प्रत्येक वर्ण को अपरकेस बना सकते हैं। या यदि आपके पास User . की सूची है ऑब्जेक्

  1. रूबी में फाइलें कैसे पढ़ें और लिखें (उदाहरण के साथ)

    आज आप रूबी में फाइलों को पढ़ना और लिखना सीखेंगे ताकि आप सामग्री को निकाल सकें, नई फाइलें बना सकें और अपनी जरूरत की जानकारी पा सकें! यहां बताया गया है कि हम क्या कवर करने जा रहे हैं : सामग्री 1 रूबी में फ़ाइलें कैसे पढ़ें 2 रूबी में फ़ाइल को कैसे लिखें 3 रूबी फ़ाइल विधियाँ 4 निर्देशिका संचालन 5

  1. स्विफ्टयूआई के साथ डिजाइन सिस्टम कैसे बनाएं

    एक उत्पाद का समर्थन करने के लिए एक डिज़ाइन सिस्टम बनाना आसान नहीं है - स्केलेबिलिटी के लिए इसे एक ही समय में मजबूत और लचीला होना चाहिए। हालांकि चुनौतीपूर्ण, बहुत सारे महान संसाधनों ने उपयोगी सिद्धांतों और दृष्टिकोणों को साझा किया है जो टीमों को दृष्टि से और प्रोग्रामेटिक रूप से एक अच्छी प्रणाली बनान