2013-08-28 29 views
6

Tôi đã đọc tất cả các bài viết tôi có thể tìm, thậm chí hiểu một vài trong số họ nhưng như một newb Python tôi vẫn còn một chút mất và hy vọng để được giúp đỡ :)nhiều đường Matching bằng Python

tôi đang làm việc trên một kịch bản để phân tích các mục quan tâm ra khỏi tệp nhật ký cụ thể của ứng dụng, mỗi dòng bắt đầu bằng dấu thời gian mà tôi có thể khớp và tôi có thể xác định hai thứ để xác định thứ tôi muốn chụp, một số nội dung một phần và chuỗi đó sẽ là chấm dứt những gì tôi muốn trích xuất.

Vấn đề của tôi là đa dòng, trong hầu hết các trường hợp mỗi dòng đăng nhập được kết thúc bằng một dòng mới nhưng một số mục chứa SQL có thể có dòng mới bên trong và do đó tạo ra các dòng mới trong nhật ký.

Vì vậy, trong một trường hợp đơn giản, tôi có thể có điều này:

[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) 

này tất cả dường như một dòng mà tôi có thể phù hợp với điều này:

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

Tuy nhiên, trong một số trường hợp có thể có dòng phá vỡ trong SQL, như vậy tôi muốn vẫn nắm bắt nó (và có khả năng thay thế các ngắt dòng với không gian). Hiện tại, tôi đang đọc tập tin tại một thời điểm rõ ràng là không hoạt động ...

  1. Tôi có cần xử lý toàn bộ tệp trong một lần không? Chúng thường có kích thước 20mb. Làm cách nào để đọc toàn bộ tệp và lặp lại thông qua tìm kiếm các khối đơn hoặc nhiều dòng?
  2. Làm cách nào để viết RegEx nhiều dòng khớp với toàn bộ nội dung trên một dòng hoặc trên toàn bộ dòng được trải rộng trên nhiều dòng?

Mục tiêu tổng thể của tôi là tham số hóa để tôi có thể sử dụng nó để trích xuất mục nhập nhật ký khớp với các mẫu khác nhau của chuỗi bắt đầu (luôn bắt đầu một dòng), chuỗi kết thúc (nơi tôi muốn chụp) và một giá trị nằm giữa chúng như một định danh.

Cảm ơn bạn đã giúp đỡ!

Chris.

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 ----" 

Bước tiếp theo, cho dòng một phần của tôi, tôi sẽ tiếp tục đọc cho đến khi tôi tìm thấy dòngEndsWith và lắp ráp các dòng vào một khối.

Tôi không có chuyên gia để đề xuất luôn được chào đón!

CẬP NHẬT - Vì vậy, tôi có nó hoạt động, nhờ vào tất cả các câu trả lời đã giúp trực tiếp mọi thứ, tôi nhận ra nó không đẹp và tôi cần dọn dẹp nếu/elif mess và làm cho nó hiệu quả hơn nhưng CNTT LÀM VIỆC! Cảm ơn vì sự giúp đỡ.

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

Bạn đã thử sử dụng cờ 're.DOTALL' chưa? Bạn sẽ phải làm cho phần '. *' Lười biếng ('. *?') Như một hệ quả của điều này mặc dù và vì bạn đang đọc từng dòng, nó sẽ hoạt động nếu bạn đọc toàn bộ tập tin cùng một lúc. Tôi không chắc chắn về ý nghĩa bộ nhớ/hiệu suất tuy nhiên. – Jerry

+0

Bạn có thể thử đọc toàn bộ tệp và sau đó tách văn bản bằng một regex phù hợp với dấu thời gian ngay sau dòng mới. Điều đó sẽ giúp bạn có được danh sách các thông điệp nhật ký đơn kề nhau, trừ khi người dùng của bạn đang nhúng những thứ như '" \ n [8/21/13 11: 30: 33: 557 PDT] "' vào SQL của họ ... trong trường hợp đó bạn có lẽ đã có một số vấn đề khác. –

+0

Tôi bắt đầu băn khoăn không biết liệu tôi có nên quay trở lại từng dòng một hay không, nếu tôi đọc một dòng tại một thời điểm, nếu dòng khớp với giá trị "bắt đầu" và "chứa" của tôi, tôi có một kết quả phù hợp sau đó cần kiểm tra điểm đánh dấu "kết thúc" hoặc nếu không có thì hãy đọc và thêm các dòng cho đến khi tôi tìm thấy nó. Tôi nghĩ tôi may mắn vì tôi biết sự bắt đầu và kết thúc sẽ luôn ở đó, tôi chỉ cần tìm chúng. – Chris

Trả lời

3

Tôi có cần xử lý toàn bộ tệp trong một lần không? Chúng thường có kích thước 20mb. Làm cách nào để đọc toàn bộ tệp và lặp lại thông qua tìm kiếm các khối đơn hoặc nhiều dòng?

Có hai tùy chọn ở đây.

Bạn có thể đọc khối tệp theo khối, đảm bảo đính kèm bất kỳ bit "còn sót lại" nào ở cuối mỗi khối vào đầu khối tiếp theo và tìm kiếm từng khối.Tất nhiên bạn sẽ phải tìm ra những gì được tính là "còn sót lại" bằng cách xem định dạng dữ liệu của bạn là gì và regex nào của bạn có thể khớp, và về lý thuyết có thể cho nhiều khối được tính như còn lại ...

Hoặc bạn có thể chỉ cần mmap tệp. Một mmap hoạt động như một byte (hoặc giống như một str trong Python 2.x), và để nó lên hệ điều hành để xử lý các khối phân trang trong và ngoài khi cần thiết. Trừ khi bạn đang cố gắng để đối phó với các tập tin hoàn toàn khổng lồ (gigabyte trong 32-bit, thậm chí nhiều trong 64-bit), đây là tầm thường và hiệu quả:

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) 

Trong các phiên bản cũ của Python, mmap không phải là một trình quản lý ngữ cảnh, vì vậy bạn cần phải quấn contextlib.closing xung quanh nó (hoặc chỉ sử dụng một cách rõ ràng close nếu bạn thích).


Làm cách nào để viết RegEx nhiều dòng khớp với toàn bộ nội dung trên một dòng hoặc trên toàn bộ dòng?

Bạn có thể sử dụng cờ DOTALL, làm cho các dòng mới phù hợp .. Thay vào đó, bạn có thể sử dụng cờ MULTILINE và đặt các ký tự $ và/hoặc ^ thích hợp vào, nhưng điều đó làm cho các trường hợp đơn giản trở nên khó khăn hơn và hiếm khi cần thiết. Dưới đây là một ví dụ với DOTALL (sử dụng một regexp đơn giản để làm cho nó rõ ràng hơn):

>>> 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'] 

Như bạn có thể nhìn thấy thứ hai .*? phù hợp newline chỉ dễ dàng như một không gian.

Nếu bạn chỉ đang cố xử lý dòng mới dưới dạng khoảng trắng, bạn cũng không cần; '\s' đã bắt được dòng mới.

Ví dụ:

>>> 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

Mẹo hay về cách sử dụng bản đồ mmap để ghi nhớ các tệp lớn –

0

Bạn có thể đọc toàn bộ tập tin vào một chuỗi và sau đó bạn có thể sử dụng re.split để tạo ra một danh sách tất cả các mục được phân cách bởi thời gian. Dưới đây là ví dụ:

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

Anh ấy được hỏi cụ thể cách tránh đọc toàn bộ tệp dưới dạng chuỗi. Vì vậy, có lẽ anh ta đã biết điều đó là có thể, và muốn biết lựa chọn thay thế là gì. – abarnert

+0

Tôi không thấy điều đó được đề cập rõ ràng ở bất cứ đâu. Các tập tin là 20 MB, đó là một trò đùa để đọc trong một lần duy nhất. – Chrismit

Các vấn đề liên quan