如何使用正则表达式处理此类字符串?

时间:2008-11-05 16:47:57

标签: python regex

如何为字符串创建正则表达式,如下所示:

<SERVER> <SERVERKEY> <COMMAND> <FOLDERPATH> <RETENTION> <TRANSFERMODE> <OUTPUTPATH> <LOGTO> <OPTIONAL-MAXSIZE> <OPTIONAL-OFFSET>

这些字段中的大多数只是简单的单词,但其中一些可以是路径,例如FOLDERPATH,OUTPUTPATH,这些路径也可以是带有文件名和通配符的路径。

保留是一个数字,传输模式可以是bin或ascii。问题是,LOGTO可以是附加了日志文件名的路径,也可以是NO,这意味着没有日志文件。

主要问题是可选参数,它们都是数字,没有MAXSIZE就不能存在OFFSET,但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 个答案:

答案 0 :(得分:4)

问题在于,因为您在文件名中允许使用空格并使用空格来分隔字段,所以解决方案不明确。您需要使用不能出现在文件名中的其他字段分隔符,或者使用其他方法来表示其中包含空格的文件名,例如:把它们放在引号中。

答案 1 :(得分:3)

从理论上说这是可能的,但是你为自己制造难以置信的事情。你有很多问题:

1)您正在使用空格作为分隔符,并且您还在路径名中允许使用空格。您可以通过强制应用程序使用不带空格的路径来避免这种情况。

2)最后有2个可选参数。这意味着对于结尾为“C:\ LogTo Path 256 300”的行,您不知道路径是C:\ LogTo路径256 300没有可选参数,还是C:\ Log To Path 256带有一个可选参数或C: \ LogTo Path有2个可选参数。

使用输出上的替换算法可以很容易地解决这个问题。用双下划线替换带下划线和下划线的空格。因此,在将空间上的日志文件拆分后,可以可靠地撤消此操作。

即使是人类,也无法100%可靠地执行此功能。

如果你假设所有路径都以星号,反斜杠或.log结尾,你可以使用正向前瞻来找到路径的末尾,但如果没有关于此的某些规则,你就会被填充。

我觉得单个正则表达式对此来说太难了,并且会让任何人试图保持代码疯狂。我是一个正则表达式的妓女,尽可能使用它们,我不会尝试这个。

答案 2 :(得分:1)

分裂空白永远不会起作用。但是,如果你可以对数据做出一些假设,那就可以使它发挥作用。

我想到的一些假设:

  • SERVERSERVERKEYCOMMAND不包含任何空格:\S+
  • FOLDERPATH以斜杠开头:/.*?
  • RETENTION是一个数字:\d+
  • TRANSFERMODE不包含任何空格:\S+
  • OUTPUTPATH以驱动器开头,以斜杠结尾:[A-Z]:\\.*?\\
  • LOGTO要么是“NO”,要么是以驱动器开头的路径:[A-Z]:\\.*?
  • MAXSIZEOFFSET是一个数字:\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'}
>>>

答案 3 :(得分:0)

您需要以正则表达式可以将它们与路径名区分开来的方式限制路径之间的字段。

所以除非你输入一个特殊的分隔符,序列

<OUTPUTPATH> <LOGTO>
带有可选空格的

将无效。

如果路径看起来像那些字段,您可能会得到令人惊讶的结果。 e.g。

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]*)?)?

使用“&gt;”分开两个路径。您可能想要引用路径名。

注意:这是匆忙完成的。

答案 4 :(得分:-1)

是否小于/大于允许的值?因为如果没有,你有一个非常简单的解决方案:

只需将“&gt;”替换为“&gt;”,在“&gt;&lt;”上拆分,并从每个项目中删除所有小于/大于的值。它可能比正则表达式代码更长,但它会更清楚发生了什么。