2008-10-28 13 views
5

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

उत्तर

11

नाम "स्कैनर", थोड़ा भ्रामक है, और है कि क्या स्कैनर के लिए है नहीं है। सभी यह है scanf() समारोह आप सी में मिल, पर्ल, एट अल के लिए एक विकल्प है। स्ट्रिंगटोकनाइज़र और split() की तरह, इसे तब तक स्कैन करने के लिए डिज़ाइन किया गया है जब तक कि किसी दिए गए पैटर्न के लिए कोई मिलान न मिल जाए, और जो कुछ भी इसे छोड़ दिया जाता है उसे टोकन के रूप में वापस किया जाता है।

एक शाब्दिक विश्लेषक, दूसरे हाथ पर, जांच करने और हर चरित्र को वर्गीकृत करने, भले ही वह केवल तय करने के लिए इसे सुरक्षित रूप से उन्हें अनदेखा कर सकते है है। इसका मतलब है कि, प्रत्येक मैच के बाद, यह कई पैटर्न लागू कर सकता है जब तक कि वह से मेल खाता है जो उस बिंदु से शुरू होता है। अन्यथा, यह अनुक्रम "//" मिल सकता है और लगता है कि यह एक टिप्पणी की शुरुआत पाई है, जब यह वास्तव में एक स्ट्रिंग अक्षर के अंदर है और यह केवल उद्घाटन उद्धरण चिह्न को नोटिस करने में विफल रहा है।

यह वास्तव में उससे कहीं अधिक जटिल है, लेकिन मैं सिर्फ यह बता रहा हूं कि क्यों स्ट्रिंगटोकनाइज़र, split() और स्कैनर जैसे अंतर्निहित टूल्स इस तरह के कार्य के लिए उपयुक्त नहीं हैं। हालांकि, लेक्सिकल विश्लेषण के सीमित रूप के लिए जावा के रेगेक्स कक्षाओं का उपयोग करना संभव है। वास्तव में, स्कैनर वर्ग के अतिरिक्त इसे नए मचर एपीआई के कारण बहुत आसान बना दिया गया था, जिसका समर्थन करने के लिए जोड़ा गया था, यानी, क्षेत्र और usePattern() विधि। जावा के रेगेक्स वर्गों के शीर्ष पर बनाए गए प्राथमिक स्कैनर का उदाहरण यहां दिया गया है।

import java.util.*; 
import java.util.regex.*; 

public class RETokenizer 
{ 
    static List<Token> tokenize(String source, List<Rule> rules) 
    { 
    List<Token> tokens = new ArrayList<Token>(); 
    int pos = 0; 
    final int end = source.length(); 
    Matcher m = Pattern.compile("dummy").matcher(source); 
    m.useTransparentBounds(true).useAnchoringBounds(false); 
    while (pos < end) 
    { 
     m.region(pos, end); 
     for (Rule r : rules) 
     { 
     if (m.usePattern(r.pattern).lookingAt()) 
     { 
      tokens.add(new Token(r.name, m.start(), m.end())); 
      pos = m.end(); 
      break; 
     } 
     } 
     pos++; // bump-along, in case no rule matched 
    } 
    return tokens; 
    } 

    static class Rule 
    { 
    final String name; 
    final Pattern pattern; 

    Rule(String name, String regex) 
    { 
     this.name = name; 
     pattern = Pattern.compile(regex); 
    } 
    } 

    static class Token 
    { 
    final String name; 
    final int startPos; 
    final int endPos; 

    Token(String name, int startPos, int endPos) 
    { 
     this.name = name; 
     this.startPos = startPos; 
     this.endPos = endPos; 
    } 

    @Override 
    public String toString() 
    { 
     return String.format("Token [%2d, %2d, %s]", startPos, endPos, name); 
    } 
    } 

    public static void main(String[] args) throws Exception 
    { 
    List<Rule> rules = new ArrayList<Rule>(); 
    rules.add(new Rule("WORD", "[A-Za-z]+")); 
    rules.add(new Rule("QUOTED", "\"[^\"]*+\"")); 
    rules.add(new Rule("COMMENT", "//.*")); 
    rules.add(new Rule("WHITESPACE", "\\s+")); 

    String str = "foo //in \"comment\"\nbar \"no //comment\" end"; 
    List<Token> result = RETokenizer.tokenize(str, rules); 
    for (Token t : result) 
    { 
     System.out.println(t); 
    } 
    } 
} 

यह, वैसे, केवल अच्छा उपयोग मैं कभी lookingAt() विधि के लिए मिल गया है है। : डी

+0

आपके पॉज़ <एंड लूप को नियमों के मिलान के मामले में 'नियमों के लिए' लूप से पहले pos बढ़ाने की आवश्यकता होगी?अन्यथा अच्छा उदाहरण और lookingAt() सुझाव के लिए धन्यवाद। –

+0

अच्छी पकड़। हां, फॉर-लूप के बाद यह 'pos ++' होना चाहिए। यह एक त्रुटि जांच के बिना एक नंगे हड्डियों का उदाहरण हो सकता है, लेकिन मुझे कम से कम यह सुनिश्चित करना चाहिए कि इसमें कोई संभावित अनंत लूप न हो। –

+0

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

3

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

अधिक सटीक जवाब सक्षम करने के लिए और अधिक आवश्यकताओं दे।

import java.util.Scanner; 


    public class Main {  

    public static void main(String[] args) { 

     String textToTokenize = "This is a text that will be tokenized. I will use 1-2 methods."; 
     Scanner scanner = new Scanner(textToTokenize); 
     scanner.useDelimiter("i."); 
     while (scanner.hasNext()){ 
      System.out.println(scanner.next()); 
     } 

     System.out.println(" **************** "); 
     String[] sSplit = textToTokenize.split("i."); 

     for (String token: sSplit){ 
      System.out.println(token); 
     } 
    } 

} 
+0

हाँ, मुझे और विस्तार करना चाहिए था। स्ट्रिंग को लगभग ** मैचों के लिए एक स्ट्रिंग ** को विभाजित करने में मददगार है, लेकिन वास्तव में रेगेक्स से मेल खाने वाले टोकन को खोजने के लिए नहीं। – eplawless

2

यदि यह एक साधारण परियोजना के लिए है (सीखने के लिए कि चीजें कैसे काम करती हैं), तो बालिंट पाटो ने क्या कहा।

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

+0

मैं भी गैर-तुच्छ के लिए जेएफएक्स की अत्यधिक अनुशंसा करता हूं। स्कैनर विनिर्देशों को लिखना कुछ अभ्यास लेता है लेकिन जेएफएक्स में अच्छी स्टार्टर फाइलें हैं और यह हासिल करने के लिए एक महान कौशल है। – Josh

2

यहां पर अधिकांश उत्तर पहले से ही उत्कृष्ट हैं, लेकिन अगर मैं ANTLR को इंगित नहीं करता तो मैं क्षमा कर दूंगा। मैंने इस उत्कृष्ट उपकरण के आसपास पूरे कंपाइलर बनाए हैं। संस्करण 3 में कुछ अद्भुत विशेषताएं हैं और मैं इसे किसी भी प्रोजेक्ट के लिए अनुशंसा करता हूं जिसके लिए आपको एक अच्छी तरह से परिभाषित व्याकरण के आधार पर इनपुट पार्स करने की आवश्यकता होती है।