2

我正在尝试使用正则表达式来解析命令行开关的键值对。这是我到目前为止所得到的:

(?<=(^-{1,2}| -{1,2}|^/| /))(?<name>[\w]+)[ :"]*(?<value>[\w.?=&+ :/|\\]*)(?=[ "]|$)

它似乎可以正确解析所有内容......几乎。如果值中有连字符,则匹配失败。如何调整它以适用于下面的所有测试示例?

测试示例(全部有效):

-s  -i:C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\ -h:local:host -d:theDB
-o:"C:\temp\db\" -s -r -host:localhost --d theDB
-s  -i:"C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB -Scripts\" -h:localhost -d:theDB
-s  -d http://www.theproject.com -h:localhost -d:theDB
-i:"C:\Users\Fozzie\Workspace\TheProject\TheProject_Stack_1_5\db\DB Scripts\" --h:localhost -d:theDB
-h:localhost -i:"C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack_1_5\db\DB Scripts\"  -d:theDB
--d theDB   -o:"C:\temp\db\" -host=local-host     -r

当值部分类似于

"C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\"

"local-host"由于其中的连字符。它被视为新开关的开始。

PS:我不想使用像 getops 这样的固定选项库。我有兴趣让正则表达式正确。

谢谢。

更新:抱歉缺少细节:这是一个 .NET 正则表达式。

4

2 回答 2

3

.NET 解决方案——投机

这个建议的 .NET 解决方案只是“建议的”;我不使用 .NET,也无法在我的任何机器上对其进行测试(是否有 .NET 的正则表达式测试网站?)。我已经采用了有效的 Perl 解决方案,删除了您不担心的部分<mark><pad>注释,并在假设 .NET 没有类似于 Perl 的x选项的易读性选项的情况下将其全部展平到一行. 您仍然可以找到对应于 Perl 正则表达式的 5 个部分的 5 组括号。我假设这(?:...)是一个非捕获组。

(?:-{1,2}|/)(?<name>\w+)(?:[=:]?|\s+)(?<value>[^-\s"][^"]*?|"[^"]*")?(?=\s+[-/]|$)

我还假设 .NET 提供了一些类似于 Perlg修饰符的机制,它允许您在第二次(或后续)遍历中扫描字符串,它在上一次遍历中停止。或者您可以以某种方式确定比赛结束的位置并从那里恢复扫描。

Perl 解决方案 — 已验证

这与我设法使用 Perl 正则表达式(在 Mac OS X 10.7.5 上使用 Perl 5.16.0 测试)一样好。

#!/usr/bin/env perl
use strict;
use warnings;

# Original regex split into 5 sections:
# C1          (?<=(^-{1,2}|\ -{1,2}|^/|\ /))
# C2          (?<name>[\w]+)
# C3          [ :"]*
# C4          (?<value>[\w.?=&+ :/|\\]*)
# C5          (?=[ "]|$)

my $rx = qr%(?<mark>  -{1,2}|/ )                        (?# Was C1)
            (?<name>  \w+ )                             (?# Was C2)
            (?<pad>   (?: [=:]?|\s+ ))                  (?# Was C3)
            (?<value> (?: [^-\s"][^"]*? | "[^"]*" ))?   (?# Was C4)
            (?=\s+[-/]|$)                               (?# Was C5)
           %x;

while (my $line = <DATA>)
{
    chomp $line;
    print "\nLine: $line\n";
    while ($line =~ m/$rx/g)
    {
        my($mark, $name, $pad, $value) = ($1, $2, $3, $4 // "");
        print "Found: mark $mark name <<$name>> pad <<$pad>> value <<$value>>\n";
    }
}

__DATA__
-s  -i:C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\ -h:local:host -d:theDB
-o:"C:\temp\db\" -s -r -host:localhost --d theDB
-s  -i:"C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB -Scripts\" -h:localhost -d:theDB
-s  -d http://www.theproject.com -h:localhost -d:theDB
-i:"C:\Users\Fozzie\Workspace\TheProject\TheProject_Stack_1_5\db\DB Scripts\" --h:localhost -d:theDB
-h:localhost -i:"C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack_1_5\db\DB Scripts\"  -d:theDB
--d theDB   -o:"C:\temp\db\" -host=local-host     -r
-s -i:C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\ -h:local:host -d:theDB
/d theDB   /o:"C:\temp\db\" /host=local-host     /r
/d theDB /o:"C:\temp\db\" /host=local-host /r /t
-s:C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\ -h:local:host -d:theDB

大部分脚本不是很有趣。__DATA__外部 while 循环一次一行读取文件的数据部分(即标记后的材料),将其打印以进行验证,然后在该行重复运行正则表达式以查找组件(标记、名称、填充和值),将它们打印出来。大部分数据是问题中提供的(谢谢!)。与最初提供的数据相比,最后三行数据是额外的。

所有的兴奋都在正则表达式中。我使用 Perl 的/x修饰符在正则表达式中允许空格以提高可读性。这意味着除非前面有反斜杠或用方括号括起来,否则空格并不重要(并且此样本中没有重要的空格)。我已经使用(?<name> ...)符号来识别原始片段,尽管名称可以省略,因为它们没有被使用。这些(?# Was Cn)部分是纯粹的评论。

  1. 标记是一个或两个破折号或斜线;--?将是另一种更短的编写方式。
  2. 名称是一串字母数字;这并不试图强制“第一个字符可能不是数字”。
  3. 填充将名称与值分开。它可以是单个等号或冒号,也可以是一串空格。内部(?: ...)是非捕获分组运算符。
  4. 该值是可选的(-s样本数据第一行第一个位置的选项没有值)。它由以下部分组成: 以破折号、双引号或空格以外的其他内容开头的字符串,后跟非引号的非贪婪字符串;或一个双引号、一串非引号和另一个双引号。
  5. 尾随零宽度上下文 (C5) 是一个或多个空格字符,后跟一个破折号或斜线,或 EOS。因为值模式是非贪婪的,所以贪婪的尾随上下文会吞噬选项值之后的尾随空格。

输出是:

Line: -s  -i:C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\ -h:local:host -d:theDB
Found: mark - name <<s>> pad << >> value <<>>
Found: mark - name <<i>> pad <<:>> value <<C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\>>
Found: mark - name <<h>> pad <<:>> value <<local:host>>
Found: mark - name <<d>> pad <<:>> value <<theDB>>

Line: -o:"C:\temp\db\" -s -r -host:localhost --d theDB
Found: mark - name <<o>> pad <<:>> value <<"C:\temp\db\">>
Found: mark - name <<s>> pad <<>> value <<>>
Found: mark - name <<r>> pad <<>> value <<>>
Found: mark - name <<host>> pad <<:>> value <<localhost>>
Found: mark -- name <<d>> pad << >> value <<theDB>>

Line: -s  -i:"C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB -Scripts\" -h:localhost -d:theDB
Found: mark - name <<s>> pad << >> value <<>>
Found: mark - name <<i>> pad <<:>> value <<"C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB -Scripts\">>
Found: mark - name <<h>> pad <<:>> value <<localhost>>
Found: mark - name <<d>> pad <<:>> value <<theDB>>

Line: -s  -d http://www.theproject.com -h:localhost -d:theDB
Found: mark - name <<s>> pad << >> value <<>>
Found: mark - name <<d>> pad << >> value <<http://www.theproject.com>>
Found: mark - name <<h>> pad <<:>> value <<localhost>>
Found: mark - name <<d>> pad <<:>> value <<theDB>>

Line: -i:"C:\Users\Fozzie\Workspace\TheProject\TheProject_Stack_1_5\db\DB Scripts\" --h:localhost -d:theDB
Found: mark - name <<i>> pad <<:>> value <<"C:\Users\Fozzie\Workspace\TheProject\TheProject_Stack_1_5\db\DB Scripts\">>
Found: mark -- name <<h>> pad <<:>> value <<localhost>>
Found: mark - name <<d>> pad <<:>> value <<theDB>>

Line: -h:localhost -i:"C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack_1_5\db\DB Scripts\"  -d:theDB
Found: mark - name <<h>> pad <<:>> value <<localhost>>
Found: mark - name <<d>> pad <<:>> value <<theDB>>

Line: --d theDB   -o:"C:\temp\db\" -host=local-host     -r
Found: mark -- name <<d>> pad << >> value <<theDB>>
Found: mark - name <<o>> pad <<:>> value <<"C:\temp\db\">>
Found: mark - name <<host>> pad <<=>> value <<local-host>>
Found: mark - name <<r>> pad <<>> value <<>>

Line: -s -i:C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\ -h:local:host -d:theDB
Found: mark - name <<s>> pad <<>> value <<>>
Found: mark - name <<i>> pad <<:>> value <<C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\>>
Found: mark - name <<h>> pad <<:>> value <<local:host>>
Found: mark - name <<d>> pad <<:>> value <<theDB>>

Line: /d theDB   /o:"C:\temp\db\" /host=local-host     /r
Found: mark / name <<d>> pad << >> value <<theDB  >>
Found: mark / name <<o>> pad <<:>> value <<"C:\temp\db\">>
Found: mark / name <<host>> pad <<=>> value <<local-host    >>
Found: mark / name <<r>> pad <<>> value <<>>

Line: /d theDB /o:"C:\temp\db\" /host=local-host /r /t
Found: mark / name <<d>> pad << >> value <<theDB>>
Found: mark / name <<o>> pad <<:>> value <<"C:\temp\db\">>
Found: mark / name <<host>> pad <<=>> value <<local-host>>
Found: mark / name <<r>> pad <<>> value <<>>
Found: mark / name <<t>> pad <<>> value <<>>

Line: -s:C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\ -h:local:host -d:theDB
Found: mark - name <<s>> pad <<:>> value <<C:\Users\Fozzie\Workspace\TheProject\TheProject-Stack-1_5\db\DB Scripts\>>
Found: mark - name <<h>> pad <<:>> value <<local:host>>
Found: mark - name <<d>> pad <<:>> value <<theDB>>
于 2012-10-30T14:37:18.073 回答
0

编辑:更改了解决方案,因此现在它可以匹配 FILEPATHS,例如 D:\huheue\hello\my name.pdf

这是我得到的正则表达式,它工作得很好。

(--?[a-zA-Z]+)[:\s=]?([A-Z]:(?:\\[\w\s-]+)+\\?(?=\s-)|\"[^\"]*\"|[^-][^\s]*)?

演示

我希望这是你所需要的,你会有这样的输出:

MATCH 1
1.  [0-2]   `-s`
MATCH 2
1.  [3-5]   `-i`
2.  [6-70]  `C:\Users\Fozzie\Workspace\TheProject\TheProaject-Stack-1_5\db\DB Scripts`
MATCH 3
1.  [80-82] `-h`
2.  [83-93] `local:host`
ETC
于 2012-10-30T12:19:28.863 回答