PHP में विशाल फ़ाइलों को सर्वर को क्रैश किए बिना पंक्ति दर पंक्ति पढ़ना

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

परिचय: "घातक त्रुटि" और 2 गीगाबाइट लॉग

\n\n

कल्पना करें: आप एक डेवलपर हैं, और आपके पास एक सिस्टम एडमिनिस्ट्रेटर पेन ड्राइव लेकर आता है। उसमें पिछले छह महीने का वेब सर्वर लॉग फ़ाइल है। आकार? मामूली 2.5 गीगाबाइट। "सभी IP पतों का पता लगाना है, जहाँ से ब्रूटफ़ोर्स के प्रयास हुए थे," वह कहता है और कॉफ़ी पीने चला जाता है।

\n\n

आप IDE खोलते हैं और यंत्रवत् लिखते हैं:

\n
$lines = file('huge_log.txt');\nforeach ($lines as $line) {\n    // पार्स कर रहे हैं...\n}\n
\n

आप "रन" दबाते हैं... और ब्राउज़र में एक सफेद स्क्रीन दिखाई देती है, और एरर लॉग में — Fatal Error: Allowed memory size exhausted। परिचित लग रहा है? यह एक क्लासिक जाल है जिसमें लगभग सभी PHP डेवलपर फँस चुके हैं।

\n\n

Stack Overflow पर "How to read a large file line by line?" प्रश्न ने सैकड़ों हज़ारों व्यूज़ बिना कारण एकत्र नहीं किए हैं। यह उन कार्यों में से एक है जहाँ "भोला" समाधान सर्वर को मार देता है, और सही दृष्टिकोण प्रोजेक्ट को बचाता है। आज हम सीखेंगे कि PHP में विशाल फ़ाइलों को मेमोरी सीमा को पार किए बिना और उच्च प्रदर्शन बनाए रखते हुए कैसे पढ़ा जाए। चलिए शुरू करते हैं!

\n\n

बड़े डेटा के लिए file() और file_get_contents() बुराई क्यों हैं

\n\n

आइए तुरंत मुख्य मिथक को दूर करें। फ़ंक्शन file() और file_get_contents() — ये लालची फ़ंक्शन हैं। ये पूरी फ़ाइल को एक साथ RAM में लोड करते हैं। यदि आपकी फ़ाइल 2 GB की है, और php.ini में memory_limit 128 MB (कई होस्टिंग के लिए मानक मान) पर सेट है, तो आपको निश्चित रूप से एक त्रुटि मिलेगी।

\n\n

इसके अलावा, PHP स्ट्रिंग्स को एन्कोडिंग में संग्रहीत करता है, जो डिस्क पर फ़ाइल आकार की तुलना में मेमोरी खपत को 2-4 गुना बढ़ा सकता है। यानी 2 GB लॉग 6-8 GB RAM "खा" सकते हैं!

\n\n

निष्कर्ष: यदि फ़ाइल का आकार आपके memory_limit के 10-20% से अधिक है, तो file() को भूल जाइए। हमें एक स्ट्रीमिंग दृष्टिकोण की आवश्यकता है।

\n\n

सही तरीका: fgets() और स्ट्रीमिंग रीडिंग

\n\n

सबसे विश्वसनीय और कुशल तरीका fopen() को fgets() के साथ उपयोग करना है। ये फ़ंक्शन फ़ाइल को पूरी तरह से नहीं, बल्कि एक बार में एक पंक्ति पढ़ते हैं। कल्पना करें कि आप एक स्ट्रॉ के माध्यम से जूस पी रहे हैं — आप एक घूंट में पूरा गिलास नहीं पीते, बल्कि थोड़ा-थोड़ा करके पीते हैं। स्ट्रीमिंग रीडिंग ठीक उसी तरह काम करती है।

\n\n

यहाँ एक बुनियादी, लेकिन पहले से काम करने वाला उदाहरण है:

\n\n
<?php\n$handle = fopen('huge_log.txt', 'r');\nif ($handle) {\n    while (($line = fgets($handle)) !== false) {\n        // एक पंक्ति प्रोसेस करें\n        echo $line;\n    }\n    fclose($handle);\n}\n?>\n
\n\n

यहाँ क्या हो रहा है? fopen() फ़ाइल खोलता है और एक पॉइंटर (resource) लौटाता है। fgets() हर बार न्यू लाइन कैरेक्टर (\\n) तक एक पंक्ति पढ़ता है। मेमोरी केवल वर्तमान पंक्ति के लिए आवंटित होती है, पूरी फ़ाइल के लिए नहीं। प्रोसेसिंग के बाद, पंक्ति गार्बेज कलेक्टर द्वारा नष्ट (या ओवरराइट) हो जाती है।

\n\n

प्रदर्शन के महत्वपूर्ण बिंदु

\n
    \n
  • बफ़र आकार: fgets() डिफ़ॉल्ट रूप से एक बार में 8192 बाइट तक पढ़ता है। यदि पंक्तियाँ लंबी हैं, तो फ़ंक्शन कई पुनरावृत्तियाँ करेगा। अधिकांश लॉग के लिए यह पर्याप्त है।
  • \n
  • false की जाँच: हमेशा सख्त तुलना !== false का उपयोग करें। क्यों? क्योंकि एक पंक्ति खाली ("") हो सकती है या इसमें "0" हो सकता है, और while ($line = fgets($handle)) समय से पहले रुक सकता है।
  • \n
  • फ़ाइल बंद करना: हमेशा fclose() को कॉल करें। हाँ, PHP स्क्रिप्ट समाप्त होने पर यह स्वयं करेगा, लेकिन यदि आप किसी लूप या बैकग्राउंड स्क्रिप्ट में फ़ाइल प्रोसेस कर रहे हैं, तो बंद न किए गए डिस्क्रिप्टर संसाधन रिसाव हैं।
  • \n
\n\n

सुपर-चार्ज: पुन: उपयोग के लिए जनरेटर (yield)

\n\n

मैन्युअल fopen/fclose प्रबंधन अच्छा है, लेकिन यदि आपको प्रोजेक्ट के विभिन्न भागों में विभिन्न फ़ाइलों को पढ़ने की आवश्यकता है, तो कोड दोहराया जाने लगेगा। यहाँ जनरेटर मदद करते हैं — PHP की सबसे सुरुचिपूर्ण विशेषताओं में से एक।

\n\n

जनरेटर आपको एक फ़ंक्शन बनाने की अनुमति देता है जो

ब्लॉग

पुस्तक अनुशंसाएँ