2013-08-28 5 views
6

मैं लेख मैं मिल सकता है के सभी पढ़ा है, यहां तक ​​कि उनमें से कुछ समझा लेकिन एक अजगर newb के रूप में मैं अभी भी एक छोटे से खो और मदद :)मल्टी लाइन मिलान

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

मेरा मुद्दा बहु-रेखा है, ज्यादातर मामलों में प्रत्येक लॉग लाइन को एक नई लाइन के साथ समाप्त कर दिया जाता है लेकिन कुछ प्रविष्टियों में एसक्यूएल होता है जिसमें इसके भीतर नई लाइनें हो सकती हैं और इसलिए लॉग में नई लाइनें बनती हैं।

तो, एक सरल मामले में मैं इस हो सकता है:

re.compile('\[(0?[1-9]|[12][0-9]|3[01])(\/)(0?[1-9]|[12][0-9]|3[01])(\/)([0-9]{2}).*(milliseconds)') 

हालांकि कुछ मामलों में लाइन हो सकता है:

[8/21/13 11:30:33:557 PDT] 00000488 SystemOut  O 21 Aug 2013 11:30:33:557 [WARN] [MXServerUI01] [CID-UIASYNC-17464] BMXAA6720W - USER = (ABCDEF) SPID = (2526) app (ITEM) object (ITEM) : select * from item where ((status != 'OBSOLETE' and itemsetid = 'ITEMSET1') and (exists (select 1 from maximo.invvendor where (exists (select 1 from maximo.companies where ((contains(name,' $AAAA ') > 0)) and (company=invvendor.manufacturer and orgid=invvendor.orgid))) and (itemnum = item.itemnum and itemsetid = item.itemsetid)))) and (itemtype in (select value from synonymdomain where domainid='ITEMTYPE' and maxvalue = 'ITEM')) order by itemnum asc (execution took 2083 milliseconds) 

यह सब एक लाइन जो मुझे इस के साथ मिलान कर सकते हैं के रूप में प्रकट होता है एसक्यूएल में तोड़ता है, जैसे कि मैं इसे अभी भी कैप्चर करना चाहता हूं (और संभावित रूप से रिक्त स्थान के साथ लाइन ब्रेक को प्रतिस्थापित करता हूं)। मैं वर्तमान में फाइल को एक समय में पढ़ रहा हूं जो स्पष्ट रूप से काम नहीं करेगा ...

  1. क्या मुझे पूरी फाइल को एक बार में संसाधित करने की आवश्यकता है? वे आमतौर पर आकार में 20 एमबी हैं। मैं पूरी फ़ाइल को कैसे पढ़ूं और इसके माध्यम से सिंगल या मल्टी-लाइन ब्लॉक की तलाश करूँ?
  2. मैं एक बहु-लाइन RegEx कैसे लिखूं जो पूरी चीज को एक पंक्ति पर मेल करेगा या इससे कई लाइनों में फैल जाएगा?

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

किसी भी मदद के लिए अग्रिम धन्यवाद!

क्रिस।

import sys, getopt, os, re 

sourceFolder = 'C:/MaxLogs' 
logFileName = sourceFolder + "/Test.log" 
lines = [] 
print "--- START ----" 
lineStartsWith = re.compile('\[(0?[1-9]|[12][0-9]|3[01])(\/)(0?[1-9]|[12][0-9]|3[01])(\/)([0-9]{2})(\)') 
lineContains = re.compile('.*BMXAA6720W.*') 
lineEndsWith = re.compile('(?:.*milliseconds.*)') 

lines = [] 
with open(logFileName, 'r') as f: 
    for line in f: 
     if lineStartsWith.match(line) and lineContains.match(line): 
      if lineEndsWith.match(line) : 
       print 'Full Line Found' 
       print line 
       print "- Record Separator -" 
      else: 
       print 'Partial Line Found' 
       print line 
       print "- Record Separator -" 

print "--- DONE ----" 

अगला कदम, मेरे आंशिक लाइन के लिए मैं पढ़ना जारी रखने के जब तक मैं lineEndsWith खोजने के लिए और एक ब्लॉक करने के लिए लाइनों को इकट्ठा करेंगे।

मैं कोई विशेषज्ञ नहीं हूं इसलिए सुझाव हमेशा स्वागत है!

अद्यतन - तो मैं यह काम कर रहा हूं, उन सभी प्रतिक्रियाओं के लिए धन्यवाद जो चीजों को निर्देशित करने में मदद करते हैं, मुझे एहसास है कि यह सुंदर नहीं है और मुझे अपने अगर/elif गड़बड़ को साफ करने और इसे अधिक कुशल बनाने की आवश्यकता है लेकिन यह काम कर रहा है! पूरी सहायताके लिए शुक्रिया।

import sys, getopt, os, re 

sourceFolder = 'C:/MaxLogs' 
logFileName = sourceFolder + "/Test.log" 

print "--- START ----" 

lineStartsWith = re.compile('\[(0?[1-9]|[12][0-9]|3[01])(\/)(0?[1-9]|[12][0-9]|3[01])(\/)([0-9]{2})(\)') 
lineContains = re.compile('.*BMXAA6720W.*') 
lineEndsWith = re.compile('(?:.*milliseconds.*)') 

lines = [] 

multiLine = False 

with open(logFileName, 'r') as f: 
    for line in f: 
     if lineStartsWith.match(line) and lineContains.match(line) and lineEndsWith.match(line): 
      lines.append(line.replace("\n", " ")) 
     elif lineStartsWith.match(line) and lineContains.match(line) and not multiLine: 
      #Found the start of a multi-line entry 
      multiLineString = line 
      multiLine = True 
     elif multiLine and not lineEndsWith.match(line): 
      multiLineString = multiLineString + line 
     elif multiLine and lineEndsWith.match(line): 
      multiLineString = multiLineString + line 
      multiLineString = multiLineString.replace("\n", " ") 
      lines.append(multiLineString) 
      multiLine = False 

for line in lines: 
    print line 
+1

क्या आपने 're.DOTALL' ध्वज का उपयोग करने का प्रयास किया है? यद्यपि आपको इसके परिणामस्वरूप '। *' भाग आलसी ('। *?') बनाना होगा, क्योंकि जब आप लाइन से लाइन पढ़ रहे हैं, तो यह काम करेगा यदि आप पूरी फाइल को एक ही बार पढ़ते हैं। हालांकि मुझे स्मृति/प्रदर्शन प्रभाव के बारे में निश्चित नहीं है। – Jerry

+0

आप पूरी फ़ाइल को पढ़ने का प्रयास कर सकते हैं और फिर टेक्स्ट को रेगेक्स के साथ विभाजित कर सकते हैं जो न्यूलाइन के बाद सीधे टाइमस्टैम्प से मेल खाता है। इससे आपको एकजुट एकल लॉग संदेशों की एक सूची मिलनी चाहिए, जब तक कि आपके उपयोगकर्ता अपने एसक्यूएल में '" \ n [8/21/13 11: 30: 33: 557 पीडीटी] "जैसी चीजें एम्बेड नहीं कर रहे हैं ... इस मामले में आप शायद कुछ अन्य समस्याएं हैं। –

+0

अब मुझे आश्चर्य हो रहा है कि क्या मैं लाइन से लाइन पर वापस जाने से बेहतर होगा जैसे कि मैंने एक समय में एक पंक्ति पढ़ी, अगर लाइन मेरे "स्टार्ट" और "इसमें" मानों से मेल खाती है तो मेरे पास एक मैच है I फिर "एंड" मार्कर की जांच करने की आवश्यकता है या, अगर यह वहां नहीं है तब तक मुझे पढ़ने और जोड़ने की लाइनें नहीं रहती हैं। मुझे लगता है कि मैं भाग्यशाली हूं कि मुझे पता है कि शुरुआत और अंत हमेशा वहां रहेगा, मुझे बस उनकी तलाश करनी है। – Chris

उत्तर

3

मैं एक ही बार में पूरी फ़ाइल पर कार्रवाई करने की जरूरत है? वे आमतौर पर आकार में 20 एमबी हैं। मैं पूरी फ़ाइल को कैसे पढ़ूं और इसके माध्यम से सिंगल या मल्टी-लाइन ब्लॉक की तलाश करूँ?

यहां दो विकल्प हैं।

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

या आप कर सकते थे फ़ाइल mmap फ़ाइल। एक mmap बाइट्स (या पायथन 2.x में एक स्ट्र की तरह) की तरह कार्य करता है, और आवश्यकतानुसार पेजिंग ब्लॉक को अंदर और बाहर संभालने के लिए इसे ओएस तक छोड़ देता है। जब तक आप पूरी तरह से बड़ी फ़ाइलें (32-बिट में गीगाबाइट, और भी अधिक 64-बिट में) से निपटने के लिए कोशिश कर रहे हैं, इस तुच्छ और कुशल है:

with open('bigfile', 'rb') as f: 
    with mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ) as m: 
     for match in compiled_re.finditer(m): 
      do_stuff(match) 

अजगर के पुराने संस्करणों में, mmap एक नहीं है संदर्भ प्रबंधक, इसलिए आपको इसके आसपास contextlib.closing लपेटना होगा (या यदि आप चाहें तो केवल एक स्पष्ट close का उपयोग करें)।


मैं एक बहु-लाइन रेगुलर एक्सप्रेशन से है कि एक लाइन पर या की यह कई पंक्तियों में फैले या तो पूरी बात से मेल खाएंगे कैसे लिख होगा?

आप DOTALL ध्वज का उपयोग कर सकते हैं, जो . न्यूलाइन से मेल खाता है। आप इसके बजाय MULTILINE ध्वज का उपयोग कर सकते हैं और उचित $ और/या ^ वर्णों को डाल सकते हैं, लेकिन इससे सरल मामलों को बहुत कठिन बना दिया जाता है, और यह शायद ही कभी आवश्यक है। यहाँ DOTALL साथ एक उदाहरण (एक सरल regexp का उपयोग कर इसे और अधिक स्पष्ट करने के लिए) है:

>>> s1 = """[8/21/13 11:30:33:557 PDT] 00000488 SystemOut  O 21 Aug 2013 11:30:33:557 [WARN] [MXServerUI01] [CID-UIASYNC-17464] BMXAA6720W - USER = (ABCDEF) SPID = (2526) app (ITEM) object (ITEM) : select * from item where ((status != 'OBSOLETE' and itemsetid = 'ITEMSET1') and (exists (select 1 from maximo.invvendor where (exists (select 1 from maximo.companies where ((contains(name,' $AAAA ') > 0)) and (company=invvendor.manufacturer and orgid=invvendor.orgid))) and (itemnum = item.itemnum and itemsetid = item.itemsetid)))) and (itemtype in (select value from synonymdomain where domainid='ITEMTYPE' and maxvalue = 'ITEM')) order by itemnum asc (execution took 2083 milliseconds)""" 
>>> s2 = """[8/21/13 11:30:33:557 PDT] 00000488 SystemOut  O 21 Aug 2013 11:30:33:557 [WARN] [MXServerUI01] [CID-UIASYNC-17464] BMXAA6720W - USER = (ABCDEF) SPID = (2526) app (ITEM) object (ITEM) : select * from item where ((status != 'OBSOLETE' and itemsetid = 'ITEMSET1') and 
    (exists (select 1 from maximo.invvendor where (exists (select 1 from maximo.companies where ((contains(name,' $AAAA ') > 0)) and (company=invvendor.manufacturer and orgid=invvendor.orgid))) and (itemnum = item.itemnum and itemsetid = item.itemsetid)))) and (itemtype in (select value from synonymdomain where domainid='ITEMTYPE' and maxvalue = 'ITEM')) order by itemnum asc (execution took 2083 milliseconds)""" 
>>> r = re.compile(r'\[(.*?)\].*?milliseconds\)', re.DOTALL) 
>>> r.findall(s1) 
['8/21/13 11:30:33:557 PDF'] 
>>> r.findall(s2) 
['8/21/13 11:30:33:557 PDF'] 

आप देख सकते हैं दूसरा .*? बस के रूप में आसानी से न्यू लाइन का मिलान नहीं हुआ एक स्थान के रूप में।

यदि आप व्हाइटस्पेस के रूप में एक नई लाइन का इलाज करने की कोशिश कर रहे हैं, तो आपको इसकी आवश्यकता नहीं है; '\s' पहले से ही न्यूलाइन पकड़ता है।

उदाहरण के लिए:

>>> s1 = 'abc def\nghi\n' 
>>> s2 = 'abc\ndef\nghi\n' 
>>> r = re.compile(r'abc\s+def') 
>>> r.findall(s1) 
['abc def'] 
>>> r.findall(s2) 
['abc\ndef'] 
+0

मेमोरी मैप में एमएमएपी का उपयोग करने के बारे में अच्छी युक्ति बड़ी फाइलें –

0

आप एक स्ट्रिंग में एक पूरी फ़ाइल पढ़ सकते हैं और फिर आप सभी प्रविष्टियों बार से अलग कर दिया की एक सूची बनाने के लिए re.split उपयोग कर सकते हैं। यहां एक उदाहरण दिया गया है:

f = open(...) 
allLines = ''.join(f.readlines()) 
entries = re.split(regex, allLines) 
+0

उन्होंने विशेष रूप से पूछा है कि पूरी फ़ाइल को स्ट्रिंग के रूप में पढ़ने से कैसे बचें। तो संभवतः वह पहले से ही जानता है कि यह संभव है, और जानना चाहता है कि विकल्प क्या है। – abarnert

+0

मुझे नहीं लगता कि स्पष्ट रूप से कहीं भी उल्लेख किया गया है। फाइलें 20 एमबी हैं, जो एक ही मजाक में पढ़ने के लिए एक मजाक है। – Chrismit

संबंधित मुद्दे