Как я могу создать регулярное выражение для такой строки:
<SERVER> <SERVERKEY> <COMMAND> <FOLDERPATH> <RETENTION> <TRANSFERMODE> <OUTPUTPATH> <LOGTO> <OPTIONAL-MAXSIZE> <OPTIONAL-OFFSET>
Большинство этих полей представляют собой простые слова, но некоторые из них могут быть путями, например FOLDERPATH, OUTPUTPATH, эти пути также могут быть путями с добавленным именем файла и подстановочным знаком.
Сохранение - это число, а режим передачи может быть bin или ascii. Проблема в том, что LOGTO может быть путем с добавленным к нему именем файла журнала или может быть NO, что означает отсутствие файла журнала.
Основная проблема - это необязательные аргументы, они оба являются числами, и OFFSET не может существовать без MAXSIZE, но MAXSIZE может существовать без смещения.
Вот несколько примеров:
loveserver love copy /muffin* 20 bin C:\Puppies\ NO 256 300
loveserver love copy /muffin* 20 bin C:\Puppies\ NO 256
loveserver love copy /hats* 300 ascii C:\Puppies\no\ C:\log\love.log 256
Теперь основная проблема заключается в том, что в путях могут быть пробелы, поэтому, если я использую. чтобы соответствовать всему, регулярное выражение перестает работать при синтаксическом анализе необязательных аргументов, когда место назначения LOG оказывается прикрепленным к пути вывода.
Также, если я в конечном итоге использую. и начните удалять его части, регулярное выражение начнет размещать вещи там, где не должно.
Вот мое регулярное выражение:
^(\s+)?(?P<SRCHOST>.+)(\s+)(?P<SRCKEY>.+)(\s+)(?P<COMMAND>COPY)(\s+)(?P<SRCDIR>.+)(\s+)(?P<RETENTION>\d+)(\s+)(?P<TRANSFER_MODE>BIN|ASC|BINARY|ASCII)(\s+)(?P<DSTDIR>.+)(\s+)(?P<LOGFILE>.+)(\s+)?(?P<SIZE>\d+)?(\s+)?(?P<OFFSET>\d+)?$






Проблема в том, что из-за того, что вы разрешаете пробелы в именах файлов и используете пробелы для разделения полей, решение неоднозначно. Вам нужно либо использовать другой символ-разделитель полей, который не может отображаться в именах файлов, либо использовать другой метод представления имен файлов с пробелами в них, например заключив их в кавычки.
теоретически это возможно, но вы сами усложняете себе задачу. Здесь у вас есть ряд проблем:
1) Вы используете пробел в качестве разделителя, а также разрешаете пробелы в именах путей. Вы можете избежать этого, заставив приложение использовать пути без пробелов.
2) В конце у вас есть 2 необязательных параметра. Это означает, что со строкой, заканчивающейся «C: \ LogTo Path 256 300», вы не знаете, является ли путь C: \ LogTo Path 256 300 без дополнительных параметров или C: \ Log To Path 256 с одним необязательным параметром или C: \ LogTo Путь с двумя необязательными параметрами.
Это легко исправить с помощью алгоритма замены на выходе. Замена пробелов на подчеркивание и подчеркивание на двойное подчеркивание. Поэтому вы можете надежно отменить это после того, как разделите файл журнала на пробелы.
Даже будучи человеком, вы не могли бы надежно выполнить эту функцию на 100%.
Если вы предполагаете, что все пути заканчиваются звездочкой, обратной косой чертой или .log, вы можете использовать положительный просмотр вперед, чтобы найти конец путей, но без каких-либо правил относительно этого вы набиты.
У меня такое чувство, что одно регулярное выражение было бы слишком сложным для этого и сводило бы с ума любого, кто пытается поддерживать код. Я шлюха регулярных выражений, использую их, когда это возможно, и я бы не стал этого делать.
Вам необходимо ограничить поля между путями таким образом, чтобы регулярное выражение могло отличить их от имен путей.
Поэтому, если вы не поставите специальный разделитель, последовательность
<OUTPUTPATH> <LOGTO>
с дополнительными пробелами работать не будет.
И если путь может выглядеть как эти поля, вы можете получить удивительные результаты. например
c:\ 12 bin \ 250 bin \output
для
<FOLDERPATH> <RETENTION> <TRANSFERMODE> <OUTPUTPATH>
неотличимо.
Итак, попробуем немного ограничить допустимые символы:
<SERVER>, <SERVERKEY>, <COMMAND> no spaces -> [^]+
<FOLDERPATH> allow anything -> .+
<RETENTION> integer -> [0-9]+
<TRANSFERMODE> allow only bin and ascii -> (bin|ascii)
<OUTPUTPATH> allow anything -> .+
<LOGTO> allow anything -> .+
<OPTIONAL-MAXSIZE>[0-9]*
<OPTIONAL-OFFSET>[0-9]*
Итак, я бы выбрал что-то вроде
[^]+ [^]+ [^]+ .+ [0-9]+ (bin|ascii) .+ \> .+( [0-9]* ( [0-9]*)?)?
С помощью ">" для разделения двух путей. Вместо этого вы можете указать пути в кавычки.
NB: Это было сделано в спешке.
Просто разделение на пробелы никогда не сработает. Но если вы можете сделать некоторые предположения относительно данных, их можно заставить работать.
Некоторые предположения, которые я имел в виду:
SERVER, SERVERKEY и COMMAND, не содержащие пробелов: \S+FOLDERPATH, начинающийся с косой черты: /.*?RETENTION является числом: \d+TRANSFERMODE без пробелов: \S+OUTPUTPATH, начинающийся с дисковода и заканчивающийся косой чертой: [A-Z]:\\.*?\\LOGTO - это либо слово "NO", либо путь, начинающийся с диска: [A-Z]:\\.*?MAXSIZE и OFFSET являются номерами: \d+Собираем все вместе:
^\s*
(?P<SERVER>\S+)\s+
(?P<SERVERKEY>\S+)\s+
(?P<COMMAND>\S+)\s+
(?P<FOLDERPATH>/.*?)\s+ # Slash not that important, but should start with non-whitespace
(?P<RETENTION>\d+)\s+
(?P<TRANSFERMODE>\S+)\s+
(?P<OUTPUTPATH>[A-Z]:\\.*?\\)\s+ # Could also support network paths
(?P<LOGTO>NO|[A-Z]:\\.*?)
(?:
\s+(?P<MAXSIZE>\d+)
(?:
\s+(?P<OFFSET>\d+)
)?
)?
\s*$
В одной строке:
^\s*(?P<SERVER>\S+)\s+(?P<SERVERKEY>\S+)\s+(?P<COMMAND>\S+)\s+(?P<FOLDERPATH>/.*?)\s+(?P<RETENTION>\d+)\s+(?P<TRANSFERMODE>\S+)\s+(?P<OUTPUTPATH>[A-Z]:\\.*?\\)\s+(?P<LOGTO>NO|[A-Z]:\\.*?)(?:\s+(?P<MAXSIZE>\d+)(?:\s+(?P<OFFSET>\d+))?)?\s*$
Тестирование:
>>> import re
>>> p = re.compile(r'^(?P<SERVER>\S+)\s+(?P<SERVERKEY>\S+)\s+(?P<COMMAND>\S+)\s+(?P<FOLDERPATH>/.*?)\s+(?P<RETENTION>\d+)\s+(?P<TRANSFERMODE>\S+)\s+(?P<OUTPUTPATH>[A-Z]:\\.*?\\)\s+(?P<LOGTO>NO|[A-Z]:\\.*?)(?:\s+(?P<MAXSIZE>\d+)(?:\s+(?P<OFFSET>\d+))?)?\s*$',re.M)
>>> data = r"""loveserver love copy /muffin* 20 bin C:\Puppies\ NO 256 300
... loveserver love copy /muffin* 20 bin C:\Puppies\ NO 256
... loveserver love copy /hats* 300 ascii C:\Puppies\no\ C:\log\love.log 256"""
>>> import pprint
>>> for match in p.finditer(data):
... print pprint.pprint(match.groupdict())
...
{'COMMAND': 'copy',
'FOLDERPATH': '/muffin*',
'LOGTO': 'NO',
'MAXSIZE': '256',
'OFFSET': '300',
'OUTPUTPATH': 'C:\\Puppies\\',
'RETENTION': '20',
'SERVER': 'loveserver',
'SERVERKEY': 'love',
'TRANSFERMODE': 'bin'}
{'COMMAND': 'copy',
'FOLDERPATH': '/muffin*',
'LOGTO': 'NO',
'MAXSIZE': '256',
'OFFSET': None,
'OUTPUTPATH': 'C:\\Puppies\\',
'RETENTION': '20',
'SERVER': 'loveserver',
'SERVERKEY': 'love',
'TRANSFERMODE': 'bin'}
{'COMMAND': 'copy',
'FOLDERPATH': '/hats*',
'LOGTO': 'C:\\log\\love.log',
'MAXSIZE': '256',
'OFFSET': None,
'OUTPUTPATH': 'C:\\Puppies\\no\\',
'RETENTION': '300',
'SERVER': 'loveserver',
'SERVERKEY': 'love',
'TRANSFERMODE': 'ascii'}
>>>
Меньше / больше разрешенного внутри значений? Потому что в противном случае у вас есть очень простое решение:
Просто замените ">" на ">", разделите на "> <" и удалите все меньше / больше из каждого элемента. Вероятно, он длиннее, чем код регулярного выражения, но будет более понятным, что происходит.
<> не используются для цитирования токенов в фактических строках - они просто указаны в спецификации формата строки запрашивающим.