delphi - डेल्फी में एक पंक्ति को पार्स करने का सबसे तेज़ तरीका क्या है?




parsing token (6)

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

मेरे पास एक बहुत बड़ी फाइल है जो मुझे रेखा से पंक्ति को पार्स करना होगा स्पीड सार का है

एक पंक्ति का उदाहरण:

Token-1   Here-is-the-Next-Token      Last-Token-on-Line
      ^                        ^
   Current                 Position
   Position              after GetToken

GetToken कहा जाता है, "यहाँ-है- अगले-टोकन" लौट रहा है और वर्तमान स्थिति को टोकन के अंतिम वर्ण की स्थिति में सेट करता है ताकि वह GetToken पर अगली कॉल के लिए तैयार हो। टोकन एक या अधिक स्थान से अलग होते हैं

मान लें कि फ़ाइल पहले से ही स्मृति में स्ट्रिंगलिस्ट में है यह स्मृति में आसानी से फिट बैठता है, 200 एमबी कहते हैं

मैं केवल पार्सिंग के लिए निष्पादन समय के बारे में चिंतित हूं। क्या कोड डेल्फी (पास्कल) में सबसे तेज़ निष्पादन का उत्पादन करेगा?


कोड लिखने का सबसे तेज़ तरीका शायद एक टेस्टलिंगलिस्ट बनाने के लिए होगा और प्रत्येक पंक्ति को कॉमटेक्स्ट संपत्ति में आपकी टेक्स्ट फ़ाइल में असाइन करेगा। डिफ़ॉल्ट रूप से, सफेद स्थान एक सीमांकक है, इसलिए आपको एक स्ट्रिंगलिस्ट आइटम प्रति टोकन मिलेगा।

MyStringList.CommaText := s;
for i := 0 to MyStringList.Count - 1 do
begin
  // process each token here
end;

आप शायद प्रत्येक लाइन खुद को पार्स करके बेहतर प्रदर्शन प्राप्त करेंगे, यद्यपि।


मैंने एक राज्य इंजिन (डीएफए) के आधार पर एक शाब्दिक विश्लेषक बनाया है यह एक मेज के साथ काम करता है और बहुत तेज है लेकिन संभव तेज़ विकल्प हैं

यह भाषा पर भी निर्भर करता है एक साधारण भाषा में संभवतः एक स्मार्ट एल्गोरिथ्म हो सकता है

तालिका प्रत्येक 2 युक्त वर्णों और 1 पूर्णांक के रिकॉर्ड की एक सरणी है। प्रत्येक टोकन के लिए लेसर तालिका के माध्यम से चलता है, स्थिति की शुरुआत 0:

state := 0;
result := tkNoToken;
while (result = tkNoToken) do begin
  if table[state].c1 > table[state].c2 then
    result := table[state].value
  else if (table[state].c1 <= c) and (c <= table[state].c2) then begin
    c := GetNextChar();
    state := table[state].value;
  end else
    Inc(state);
end;

यह सरल है और एक जादू की तरह काम करता है


यह एक और सवाल पूछता है - बड़ा कैसे? हमें लाइनों या # या एमबी (जीबी) की तरह एक सुराग दे दो? तो हम जानते होंगे कि यह स्मृति में फिट है, डिस्क आधारित आदि की आवश्यकता है।

पहले पास में मैं अपने WordList (एस: स्ट्रिंग; AList: TStringlist) का उपयोग करेगा;

तो आप प्रत्येक टोकन को अलिस्ट [एन] के रूप में एक्सेस कर सकते हैं ... या उन्हें या जो कुछ भी सॉर्ट कर सकते हैं


स्पीड हमेशा उस समय के सापेक्ष रहेगा जब आप एक बार इसे पार्स किए जाते हैं। आकार के बावजूद एक शब्दलेखक पार्सर टेक्स्ट स्ट्रीम से टोकन में परिवर्तित करने का सबसे तेज़ तरीका है। क्लास यूनिट में टीपेर्स शुरू करने के लिए एक शानदार जगह है।

निजी तौर पर यह एक समय था जब मुझे एक पार्सर लिखना पड़ता था, लेकिन फिर से एक और तारीख की कोशिश की और सही तरीके से एक व्याकरण बनाने के लिए LEX / YACC का इस्तेमाल किया जायेगा, तो यह व्याकरण को आपके कोड में परिवर्तित करने के लिए उपयोग किया जा सकता है, जिससे आप अपनी प्रसंस्करण कर सकते हैं। DYacc एक डेल्फी संस्करण है ... सुनिश्चित नहीं है कि यह अभी भी संकलित है या नहीं, लेकिन एक नज़र के लायक यदि आप चीजों को पुराने स्कूल करना चाहते हैं। यहां ड्रैगन बुक बड़ी मदद होगी, अगर आपको एक कॉपी मिल सकती है


  • प्रसंस्करण की गति के लिए PChar वृद्धिशील का उपयोग करें
  • यदि कुछ टोकन की आवश्यकता नहीं है, तो केवल मांग पर टोकन डेटा को कॉपी करें
  • PChar को स्थानीय चर में कॉपी करें जब वास्तव में वर्णों के माध्यम से स्कैन किया जाए
  • एक बफर में स्रोत डेटा रखें जब तक कि आपको लाइन द्वारा लाइन को संभालना न हो, और फिर भी, लेक्ज़र पहचानकर्ता में एक अलग टोकन के रूप में लाइन प्रसंस्करण संभालने पर विचार करें
  • एक बाइट सरणी बफ़र संसाधित करने पर विचार करें जो फ़ाइल से सीधे आ गया है, यदि आप निश्चित रूप से एन्कोडिंग जानते हैं; यदि डेल्फी 200 9 का इस्तेमाल करते हैं, तो पीसीहर के बजाय पंसीर का प्रयोग करें, जब तक कि आपको पता नहीं है कि एन्कोडिंग यूटीएफ 16-एलई है।
  • यदि आप जानते हैं कि केवल सफेद स्थान # 32 (एएससीआईआई स्पेस) या वर्णों के एक समान सीमित सेट होने जा रहे हैं, तो कुछ चालाक बिट हेरिपल हैक्स हो सकते हैं जो आपको इंटिगर स्कैनिंग का उपयोग करते हुए एक समय में 4 बाइट्स प्रोसेस कर सकते हैं। मैं यहां बड़ी जीत की अपेक्षा नहीं करता था, और यह कोड कीचड़ के रूप में स्पष्ट होगा।

यहां एक नमूना लेसर है जो बहुत कुशल होना चाहिए, लेकिन यह मानता है कि सभी स्रोत डेटा एक स्ट्रिंग में है बहुत लंबे टोकन के कारण बफ़र्स को संभालने के लिए फिर से काम करना मुश्किल है।

type
  TLexer = class
  private
    FData: string;
    FTokenStart: PChar;
    FCurrPos: PChar;
    function GetCurrentToken: string;
  public
    constructor Create(const AData: string);
    function GetNextToken: Boolean;
    property CurrentToken: string read GetCurrentToken;
  end;

{ TLexer }

constructor TLexer.Create(const AData: string);
begin
  FData := AData;
  FCurrPos := PChar(FData);
end;

function TLexer.GetCurrentToken: string;
begin
  SetString(Result, FTokenStart, FCurrPos - FTokenStart);
end;

function TLexer.GetNextToken: Boolean;
var
  cp: PChar;
begin
  cp := FCurrPos; // copy to local to permit register allocation

  // skip whitespace; this test could be converted to an unsigned int
  // subtraction and compare for only a single branch
  while (cp^ > #0) and (cp^ <= #32) do
    Inc(cp);

  // using null terminater for end of file
  Result := cp^ <> #0;

  if Result then
  begin
    FTokenStart := cp;
    Inc(cp);
    while cp^ > #32 do
      Inc(cp);
  end;

  FCurrPos := cp;
end;






pascal