【问题标题】:RegEx function to parse a command line without using a libraryRegEx 函数在不使用库的情况下解析命令行
【发布时间】:2013-06-07 17:54:45
【问题描述】:

我想使用空格作为分隔符来拆分字符串,但是如果有多个单词用双引号或单引号括起来,那么我希望它们作为一个项目返回。

例如如果输入字符串是:

CALL "C:\My File Name With Space" /P1 P1Value /P1 P2Value

输出数组将是:

Array[0]=Call
Array[1]=C:\My File Name With Space
Array[2]=/P1
Array[3]=P1Value
Array[4]=/P1
Array[5]=P2Value

你如何使用正则表达式来做到这一点?我意识到有命令行解析器。我粗略地看了一个流行的,但它没有处理可以有多个同名参数的情况。无论如何,而不是学习如何使用命令行解析库(将其留到另一天)。我有兴趣更多地接触 RegEx 函数。

你将如何使用 RegEx 函数来解析这个?

【问题讨论】:

  • 在 Main() 中不是将命令行参数作为字符串数组提供给您吗?
  • 不,我正在解析文件夹中的批处理文件。
  • 我不会使用正则表达式来处理这个问题。命令行中的特殊情况太多了。您最好使用来自stackoverflow.com/questions/491595/… 的建议之一,或者只编写自己的建议(这可能需要几个小时)。
  • 实际上,我认为是 NDesk 不支持具有相同名称的多个参数(我可能是错的。)我感觉 RegEx 可以处理指定的 2 个要求标准。这就是我要找的。​​span>
  • 问题比听起来更难。解析包含引号的 Windows 命令行非常奇怪。有关示例,请参阅blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx

标签: c# regex


【解决方案1】:

Jim Mischel 评论中的link 指出Win32 API 为此提供了一个函数。我建议使用它来保持一致性。这是一个示例(来自PInvoke)。

static string[] SplitArgs(string unsplitArgumentLine)
{
    int numberOfArgs;
    IntPtr ptrToSplitArgs;
    string[] splitArgs;

    ptrToSplitArgs = CommandLineToArgvW(unsplitArgumentLine, out numberOfArgs);
    if (ptrToSplitArgs == IntPtr.Zero)
        throw new ArgumentException("Unable to split argument.",
          new Win32Exception());
    try
    {
        splitArgs = new string[numberOfArgs];
        for (int i = 0; i < numberOfArgs; i++)
            splitArgs[i] = Marshal.PtrToStringUni(
                Marshal.ReadIntPtr(ptrToSplitArgs, i * IntPtr.Size));
        return splitArgs;
    }
    finally
    {
        LocalFree(ptrToSplitArgs);
    }
}

[DllImport("shell32.dll", SetLastError = true)]
static extern IntPtr CommandLineToArgvW(
    [MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine,
    out int pNumArgs);

[DllImport("kernel32.dll")]
static extern IntPtr LocalFree(IntPtr hMem);

如果你想要一个快速而肮脏、不灵活、脆弱的正则表达式解决方案,你可以这样做:

var rex = new Regex(@"("".*?""|[^ ""]+)+");
string test = "CALL \"C:\\My File Name With Space\" /P1 P1Value /P1 P2Value";
var array = rex.Matches(test).OfType<Match>().Select(m => m.Groups[0]).ToArray();

【讨论】:

  • 工作就像一个魅力。我很惊讶地看到代码超出了框架。我觉得有点脏,不知道为什么,可能是我不明白。
  • sqlcmd.exe (msdn.microsoft.com/en-us/library/ms162773.aspx) 和可能的其他 exe 允许以破折号后跟单个字母的形式进行参数切换,以便在写入参数值之前有一个可选空格。例如“sqlcmd.exe -sMyServer”和“sqlcmd.exe -s MyServer”表示相同的传递值。但是,此函数为第一个传递 2 个参数,为第二个传递 3 个参数。
  • @ChadD - CommandLineToArgvW 是 shell 用来确定如何传递参数的工具。 sqlcmd.exe 然后包含解释它们的逻辑。 -s MyServer 作为两个参数传递,但 sqlcmd.exe 将它们识别为一个选项。
  • CommandLineToArgvW 解决方案不起作用,因为它不尊重 \\ 和 \" 等特殊情况
【解决方案2】:

@chad Henderson,您忘记包含单引号,这也存在捕获一组引号之前的任何内容的问题。

这里是包含单引号的更正,但也显示了引号前额外捕获的问题。 http://regexhero.net/tester/?id=81cebbb2-5548-4973-be19-b508f14c3348

【讨论】:

  • Windows 实际上并不像对待双引号那样对待单引号。而且您不确定引号的类型在您的正则表达式中匹配:)。只是为了好玩,我更新了我的以支持 a"b c"d 形式的参数
  • 我很好奇 windows 处理单引号的方式与此有什么关系?
  • Windows 将 'a b' 视为两个单独的参数,'ab'
【解决方案3】:

由于上述各种原因,我不会使用正则表达式。

如果我确实需要,这将符合您的简单要求:

(".*?")|([^ ]+)

但是,这不包括:

  • 转义引号
  • 单引号
  • 非 ascii 引号(您认为人们不会将 word 中的智能引号粘贴到您的文件中吗?)
  • 以上组合

这只是我的想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-04
    • 2021-06-23
    • 1970-01-01
    • 2012-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多