Мне нужно проанализировать некоторый файл журнала, и он выглядит ниже, я хотел бы получить 3 части данных,
Я использую это регулярное выражение, но оно не получает третьей части.
(\d\d:\d\d:\d\d).*(ABC|DEF).*\\(\d\w\.?\w\..*)\soutput.*
Любое предложение будет оценено.
08:38:36 TestModule - [INFO]result success !! ftp_site=ftp.test.com file_dir=CPY input file=\root\level1\level2-ABC\2C.013000000B.dat output file=c:\local\project1\data\2C.013000000B.dat.ext
06:40:37 TestModule - [INFO]result success !! ftp_site=ftp.test.com file_dir=CPY input file=\root\level1\level2-ABC\20100722B.TXT output file=c:\local\project1\data\20100722B.TXT.ext
06:40:39 TestModule - [INFO]result success !! ftp_site=ftp.test.com file_dir=CPY input file=\root\level1\level2-DEF\20100722D1-XYZ.TXT output file=c:\local\project1\data\20100722D1-YFP.TXT.ext
06:40:42 TestModule - [INFO]result success !! ftp_site=ftp.test.com file_dir=CPY input file=\root\level1\level2-DEF\2C.250B output file=c:\local\project1\data\2C.250B.ext
BR
Эдвард
Регулярные выражения очень хороши при решении таких проблем, как этот, например, анализ записей в файлах журналов. Ответ MarcoS прекрасно решает вашу проблему. Однако другой подход заключается в написании обобщенной функции (многократного использования), которая разлагает запись файла журнала в ее различные компоненты и возвращает объект соответствия, содержащий все эти анализируемые компоненты. После разложения тесты могут быть легко применены к составным частям для проверки различных требований (например, путь входного файла должен заканчиваться на ABC
или DEF
). Вот питон script, который имеет такую функцию: decomposeLogEntry()
и демонстрирует, как использовать его для решения вашей проблемы:
import re
def decomposeLogEntry(text):
r""" Decompose log file entry into its various components.
If text is a valid log entry, return regex match object of
log entry components strings. Otherwise return None."""
return re.match(r"""
# Decompose log file entry into its various components.
^ # Anchor to start of string
(?P<time>\d\d:\d\d:\d\d) # Capture: time
\s+
(?P<modname>\w+?) # Capture module name
\s-\s\[
(?P<msgtype>[^]]+) # Capture message type
\]
(?P<message>[^!]+) # Capture message text
!!\sftp_site=
(?P<ftpsite>\S+?) # Capture ftp URL
\sfile_dir=
(?P<filedir>\S+?) # Capture file directory?
\sinput\sfile=
(?P<infile> # Capture input path and filename
(?P<infilepath>\S+)\\ # Capture input file path
(?P<infilename>[^\s\\]+) # Capture input file filename
)
\soutput\sfile=
(?P<outfile> # Capture input path and filename
(?P<outfilepath>\S+)\\ # Capture output file path
(?P<outfilename>[^\s\\]+) # Capture output file filename
)
\s* # Optional whitespace at end.
$ # Anchor to end of string
""", text, re.IGNORECASE | re.VERBOSE)
# Demonstrate decomposeLogEntry function. Print components of all log entries.
f=open("testdata.log")
mcnt = 0
for line in f:
# Decompose this line into its components.
m = decomposeLogEntry(line)
if m:
mcnt += 1
print "Match number %d" % (mcnt)
print " Time: %s" % m.group("time")
print " Module name: %s" % m.group("modname")
print " Message type: %s" % m.group("time")
print " Message: %s" % m.group("message")
print " FTP site URL: %s" % m.group("ftpsite")
print " Input file: %s" % m.group("infile")
print " Input file path: %s" % m.group("infilepath")
print " Input file name: %s" % m.group("infilename")
print " Output file: %s" % m.group("outfile")
print " Output file path: %s" % m.group("outfilepath")
print " Output file name: %s" % m.group("outfilename")
print "\n",
f.close()
# Next pick out only the desired data.
f=open("testdata.log")
mcnt = 0
matches = []
for line in f:
# Decompose this line into its components.
m = decomposeLogEntry(line)
if m:
# See if this record meets desired requirements
if re.search(r"ABC$|DEF$", m.group("infilepath")):
matches.append(line)
f.close()
print "There were %d matching records" % len(matches)
Эта функция не только выделяет интересующие вас детали, но также проверяет входные данные и отклоняет плохо отформатированные записи. После написания и отладки эта функция может быть повторно использована другими программами, которые должны анализировать файлы журналов для других требований.
Вот результат работы script при применении к вашим тестовым данным:
r"""
Match number 1
Time: 08:38:36
Module name: TestModule
Message type: 08:38:36
Message: result success
FTP site URL: ftp.test.com
Input file: \root\level1\level2-ABC\2C.013000000B.dat
Input file path: \root\level1\level2-ABC
Input file name: 2C.013000000B.dat
Output file: c:\local\project1\data\2C.013000000B.dat.ext
Output file path: c:\local\project1\data
Output file name: 2C.013000000B.dat.ext
Match number 2
Time: 06:40:37
Module name: TestModule
Message type: 06:40:37
Message: result success
FTP site URL: ftp.test.com
Input file: \root\level1\level2-ABC\20100722B.TXT
Input file path: \root\level1\level2-ABC
Input file name: 20100722B.TXT
Output file: c:\local\project1\data\20100722B.TXT.ext
Output file path: c:\local\project1\data
Output file name: 20100722B.TXT.ext
Match number 3
Time: 06:40:39
Module name: TestModule
Message type: 06:40:39
Message: result success
FTP site URL: ftp.test.com
Input file: \root\level1\level2-DEF\20100722D1-XYZ.TXT
Input file path: \root\level1\level2-DEF
Input file name: 20100722D1-XYZ.TXT
Output file: c:\local\project1\data\20100722D1-YFP.TXT.ext
Output file path: c:\local\project1\data
Output file name: 20100722D1-YFP.TXT.ext
Match number 4
Time: 06:40:42
Module name: TestModule
Message type: 06:40:42
Message: result success
FTP site URL: ftp.test.com
Input file: \root\level1\level2-DEF\2C.250B
Input file path: \root\level1\level2-DEF
Input file name: 2C.250B
Output file: c:\local\project1\data\2C.250B.ext
Output file path: c:\local\project1\data
Output file name: 2C.250B.ext
There were 4 matching records
"""
Использование split - хорошая идея. Если вы действительно хотите регулярное выражение, я бы сделал это следующим образом:
(\d\d:\d\d:\d\d).*?input file=.*?(ABC|DEF)\\\\(.*?)\soutput
Проверьте здесь
Вы можете сделать это просто с помощью обычной обработки строк
f=open("file")
for line in f:
date,b = line.split("input")
print "time: " , date.split()[0]
input_path = b.split("output")[0]
tokens=input_path.split("\\")
filename=tokens[-1]
directory=tokens[-2].split("-")[-1]
print filename, directory
f.close()
Почему regex?
Рассмотрите возможность использования split
для получения всех слов. Это даст вам временную метку. Затем пройдите все остальные слова, проверьте, есть ли в них =
, разделите их снова и в этом случае у вас есть свои пути и другие параметры. Стандартная обработка пути Python (os.path
) поможет вам при получении имен папок и файлов.
Конечно, этот подход терпит неудачу, если ваши имена путей могут содержать пробелы, но в противном случае это определенно стоит рассмотреть.
Если вы используете инструмент регулярных выражений, это облегчит вам задачу поиска и устранения неполадок regex. Попробуйте этот бесплатный - есть, вероятно, лучшие, но это отлично работает. Вы можете вставить свой файл журнала там и попробовать свое регулярное выражение за раз, и он будет выделять совпадения в режиме реального времени.
Это сработало для ваших примеров:
r'(\d\d:\d\d:\d\d).*(ABC|DEF).*?([^\\]*)\soutput.*'
Хотя хорошо написанное регулярное выражение подходит здесь, я бы подошел к этому по-другому. В частности, os.path.split
предназначен для разделения имен файлов из базовых путей и касается всех случаев, которые игнорируются этим регулярным выражением.