2025, Oct 04 19:32

दो लाइन पीछे देखकर truck से जुड़े मार्कर निकालें (Python)

अर्ध-संरचित लॉग्स से truck मिलान पर दो लाइन पहले आने वाला तीसरा टोकन कैसे निकालें: लुक-बिहाइंड स्लाइडिंग विंडो, deque और Python के साथ सटीक, एक-पास समाधान, उदाहरण सहित।

अर्ध-संरचित लॉग्स को पार्स करते समय अक्सर किसी मिलान के संदर्भ में पीछे या आगे देखना पड़ता है। यहाँ काम पूरी तरह सटीक है: हर वह पंक्ति ढूँढें जहाँ दूसरा टोकन truck के बराबर हो, और फिर उससे दो पंक्तियाँ पहले आने वाली लाइन से तीसरा टोकन निकालें। इनपुट एक साधारण टेक्स्ट फ़ाइल है जिसमें प्लस साइन वाली लाइनों से अलग-अलग वैल्यू-ब्लॉक विभाजित हैं, और अपेक्षित आउटपुट उन मार्कर वैल्यूज़ की सूची है जो मिलान वाली पंक्तियों से दो लाइन ऊपर होती हैं।

समस्या की रूपरेखा

ऐसा इनपुट होने पर हमें beta और delta लौटाने हैं, क्योंकि truck दोनों बार उन मार्करों के ठीक दो लाइनों बाद आता है:

apples    grapes    alpha   pears
chicago paris london 
yellow    blue      red
+++++++++++++++++++++
apples    grapes    beta   pears
chicago paris london 
car   truck  van
+++++++++++++++++++
apples    grapes    gamma   pears
chicago paris london 
white  purple   black
+++++++++++++++++++
apples    grapes    delta   pears
chicago paris london 
car   truck  van

प्रारंभिक प्रयास (और वह क्यों पर्याप्त नहीं)

आम तौर पर हम फ़ाइल स्कैन करके उन पंक्तियों को इकट्ठा करने से शुरू करते हैं जिनमें दूसरा टोकन truck के बराबर है, और फिर उन्हें pandas DataFrame में डाल देते हैं। लेकिन यह तरीका सिर्फ़ मिलान हुई लाइनों को समेटता है, उन वैल्यूज़ को नहीं जो उनसे दो लाइन पहले हैं। यानी आवश्यक संदर्भ विंडो बची ही नहीं रहती।

import pandas as pd
rows_buffer = []
with open('input.txt', 'r') as fh:
    for record in fh:
        tokens = record.split()
        if len(tokens) > 1 and tokens[1] == "truck":
            rows_buffer.append(tokens)
frame = pd.DataFrame(rows_buffer)
print(frame.to_string)

यह केवल मिलान वाली पंक्तियों को इकट्ठा करता है। दो लाइन ऊपर से तीसरा टोकन निकालने के लिए पढ़ते समय ही लाइनों पर एक लुक-बिहाइंड विंडो बनाए रखना होगा।

असल में हो क्या रहा है

यह आवश्यकता पूरी तरह स्थितिगत और सापेक्ष है। जब वर्तमान पंक्ति का दूसरा टोकन truck होता है, तब हमें जिस वैल्यू की ज़रूरत है, वह दो पंक्तियाँ पहले वाली लाइन के तीसरे टोकन में रहती है। इसलिए इटरेशन के दौरान हाल की लाइनों का एक स्लाइडिंग विंडो संभालकर रखना ज़रूरी है। बाद में सीधे DataFrame में पंक्तियाँ चुन लेने से काम नहीं चलेगा, जब तक आस-पास का संदर्भ भी न रखा जाए।

लुक-बिहाइंड विंडो के साथ सटीक समाधान

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

#!/usr/bin/env python
import sys
from collections import deque
def run():
    if len(sys.argv) < 2:
        print("Usage: script_runner.py inputPath", file=sys.stderr)
        sys.exit(1)
    LOOKBACK = 3
    ring = deque(maxlen=LOOKBACK)
    hits = []
    with open(sys.argv[1], 'r') as src:
        for entry in src:
            parts = entry.strip().split()
            if len(parts) < 2:
                # मिलान के लिए पर्याप्त फ़ील्ड नहीं; अप्रासंगिक पंक्ति
                ring.append(entry)
                continue
            ring.append(entry)
            if len(ring) == LOOKBACK and parts[1] == "truck":
                target_line = ring[0]
                target_parts = target_line.split()
                if len(target_parts) >= 3:
                    hits.append(target_parts[2])
    if hits:
        print(hits)
        # वैकल्पिक: परिणाम को DataFrame में बदलें
        # import pandas as pd
        # df_out = pd.DataFrame(hits, columns=['lookbehind'])
        # print(df_out)
if __name__ == "__main__":
    run()

दिए गए इनपुट पर यह ['beta', 'delta'] प्रिंट करता है, जो अपेक्षित आउटपुट से मेल खाता है। रिंग बफ़र ठीक वही संदर्भ बनाए रखता है जिसकी हमें ज़रूरत है, और “दो लाइन ऊपर” देखना सरल और सुरक्षित बन जाता है।

यह क्यों महत्वपूर्ण है

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

मुख्य सीख

जब आवश्यकता लाइनों के बीच सापेक्ष स्थिति पर निर्भर हो, तो पढ़ते समय एक छोटी स्लाइडिंग विंडो में संदर्भ बनाए रखें। इससे अनावश्यक बड़े इन-मेमोरी स्ट्रक्चर जल्दी बनाने की ज़रूरत नहीं पड़ती और एक ही पास में ठीक वही निकाला जा सकता है जिसकी ज़रूरत है। यदि बाद में DataFrame चाहिए, तो स्ट्रीमिंग के बाद निकाली गई वैल्यूज़ की सूची को रूपांतरित कर लें।

यह लेख StackOverflow पर प्रश्न (लेखक: yodish) और ticktalk द्वारा दिए गए उत्तर पर आधारित है।