用于解析命令行参数的正则表达式失败

时间:2012-10-30 11:48:24

标签: .net regex command-line

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

(?<=(^-{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正则表达式。

2 个答案:

答案 0 :(得分:3)

.NET解决方案 - 推测

这个建议的.NET解决方案只是'建议';我不做.NET而且无法在我的任何机器上测试它(是否有针对.NET的正则表达式测试网站?)。我已经采用了Perl解决方案,删除了您不担心的<mark><pad>部分以及评论,并假设.NET没有将其全部压平为一行t有一个易读性选项,类似于Perl的x选项。您仍然可以找到与Perl正则表达式的5个部分相对应的5组括号。我假设(?:...)是一个非捕获组。

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

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

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

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

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

  1. 标记是一个或两个破折号或斜线; --?将是另一种更短的写作方式。
  2. 这个名字是一串字母数字;这并不试图强制执行'第一个字符可能不是数字'。
  3. pad将名称与值分开。它可以是单个等号或冒号,也可以是一串空格。内部(?: ...)是非捕获分组操作符。
  4. 该值是可选的(样本数据第一行第一个位置的-s选项没有值)。它包括:一个以短划线,双引号或空格开头的字符串,后跟非贪婪的非引号字符串;或双引号,一串非引号和另一个双引号。
  5. 尾随零宽度上下文(C5)是一个或多个空格字符后跟短划线或斜线或EOS。因为值模式是非贪婪的,所以贪婪的尾随上下文会在选项值之后吞噬尾随空格。
  6. 输出结果为:

    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>>
    

答案 1 :(得分:0)

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

这是我得到的正则表达式,效果非常好。

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

Demo

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

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