Как мне обработать такую ​​строку с помощью регулярных выражений? - PullRequest
0 голосов
/ 05 ноября 2008

Как я могу создать регулярное выражение для строки, такой как это:

<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+)?$

Ответы [ 5 ]

4 голосов
/ 05 ноября 2008

Проблема в том, что, поскольку вы допускаете пробелы в именах файлов и используете пробелы для разделения полей, решение неоднозначно. Вам нужно либо использовать другой символ-разделитель полей, который не может появляться в именах файлов, либо использовать какой-либо другой метод представления имен файлов с пробелами в них, например, поставить их в кавычки.

3 голосов
/ 05 ноября 2008

теоретически это возможно, но вы делаете вещи невероятно сложными для себя. У вас есть несколько проблем здесь:

1) Вы используете пробел в качестве разделителя, и вы также разрешаете пробелы в именах путей. Вы можете избежать этого, заставив приложение использовать пути без пробелов.

2) У вас есть 2 дополнительных параметра в конце. Это означает, что со строкой, заканчивающейся «C: \ LogTo Path 256 300», вы не знаете, является ли путь C: \ LogTo Path 256 300 без дополнительных параметров или C: \ Log To Path 256 с одним необязательным параметром или C: \ LogTo Path с 2 дополнительными параметрами.

Это легко исправить с помощью алгоритма замены на выходе. Замена пробелов подчеркиванием и подчеркиванием двойным подчеркиванием. Поэтому вы можете надежно изменить это после разделения файла журнала на пробелы.

Даже будучи человеком, вы не могли надежно выполнить эту функцию на 100%.

Если вы предполагаете, что все пути заканчиваются звездочкой, обратной косой чертой или .log, вы можете использовать положительный прогноз, чтобы найти конец путей, но без каких-либо правил, касающихся этого, вы набиты.

У меня такое ощущение, что одно регулярное выражение будет слишком сложным для этого и сделает любого, кто пытается сохранить код безумным. Я - шлюха с регулярными выражениями, использую их всякий раз, когда это возможно, и я бы не стал пытаться это делать.

1 голос
/ 05 ноября 2008

Простое разбиение на пустые места никогда не сработает. Но если вы можете сделать некоторые предположения относительно данных, это может быть сделано для работы.

Некоторые предположения, которые я имел в виду:

  • 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'}
>>>
0 голосов
/ 05 ноября 2008

Меньше / больше, чем разрешено внутри значений? Потому что, если нет, у вас есть очень простое решение:

Просто замените любое вхождение ">" на ">", разделите на "> <" и удалите все, что меньше / больше, чем у каждого элемента. Вероятно, он длиннее кода регулярного выражения, но он будет более понятным, что происходит. </p>

0 голосов
/ 05 ноября 2008

Вам необходимо ограничить поля между путями таким образом, чтобы регулярное выражение могло отличить их от путей.

Так что, если вы не вставите специальный разделитель, последовательность

<OUTPUTPATH> <LOGTO>

с дополнительными пробелами не будет работать.

И если путь может выглядеть как эти поля, вы можете получить удивительные результаты. например, * +1008 *

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. Это было сделано в спешке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...