行参数转换为C#中的字符串[]

我有一个包含要传递给另一个可执行文件的命令行参数的单个字符串,并且需要以与C#中命令相同的方式提取包含各个参数的字符串[],如果命令已在命令行上指定。 当通过反射执行另一个组件入口点时,将使用字符串[]。

有这个标准功能吗? 还是有一个首选的方法(正则表达式?)正确分裂参数? 它必须处理可能正确包含空格的'''分隔字符串,所以我不能仅仅分割''。

示例字符串:

string parameterString = @"/src:""C:tmpSome FolderSub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam foo";

示例结果:

string[] parameterArray = new string[] { 
  @"/src:C:tmpSome FolderSub Folder",
  @"/users:abcdefg@hijkl.com",
  @"tasks:SomeTask,Some Other Task",
  @"-someParam",
  @"foo"
};

我不需要命令行解析库,只是一种获取应该生成的String []的方法。

更新 :我不得不改变预期的结果来匹配C#实际生成的内容(删除了拆分字符串中的额外内容)


除了Earwicker提供的良好且纯粹的托管解决方案之外,为了完整起见,值得一提的是,Windows还提供了CommandLineToArgvW函数,用于将字符串分解为字符串数组:

LPWSTR *CommandLineToArgvW(
    LPCWSTR lpCmdLine, int *pNumArgs);

解析一个Unicode命令行字符串,并以类似于标准C运行时argv和argc值的方式返回一个指向命令行参数的指针数组以及此类参数的计数。

使用CommandLineToArgvW()API将命令行字符串转换为参数[],可以找到从C#中调用此API并将结果字符串数组解压缩到托管代码中的示例。“下面是相同代码的稍微简单的版本:

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

public static string[] CommandLineToArgs(string commandLine)
{
    int argc;
    var argv = CommandLineToArgvW(commandLine, out argc);        
    if (argv == IntPtr.Zero)
        throw new System.ComponentModel.Win32Exception();
    try
    {
        var args = new string[argc];
        for (var i = 0; i < args.Length; i++)
        {
            var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
            args[i] = Marshal.PtrToStringUni(p);
        }

        return args;
    }
    finally
    {
        Marshal.FreeHGlobal(argv);
    }
}

它让我很烦恼,没有函数根据检查每个字符的函数来分割字符串。 如果有的话,你可以这样写:

    public static IEnumerable<string> SplitCommandLine(string commandLine)
    {
        bool inQuotes = false;

        return commandLine.Split(c =>
                                 {
                                     if (c == '"')
                                         inQuotes = !inQuotes;

                                     return !inQuotes && c == ' ';
                                 })
                          .Select(arg => arg.Trim().TrimMatchingQuotes('"'))
                          .Where(arg => !string.IsNullOrEmpty(arg));
    }

虽然已经写了,为什么不写出必要的扩展方法。 好吧,你说了我的话......

首先,我自己的Split版本需要一个函数来决定指定的字符是否应该拆分字符串:

    public static IEnumerable<string> Split(this string str, 
                                            Func<char, bool> controller)
    {
        int nextPiece = 0;

        for (int c = 0; c < str.Length; c++)
        {
            if (controller(str[c]))
            {
                yield return str.Substring(nextPiece, c - nextPiece);
                nextPiece = c + 1;
            }
        }

        yield return str.Substring(nextPiece);
    }

它可能会根据情况产生一些空字符串,但也许这些信息在其他情况下会很有用,所以我不会删除此函数中的空条目。

其次(也更平凡)一个小帮手,它将从字符串的开始和结尾修剪一对匹配的引号。 它比标准修剪方法更挑剔 - 它只会从每一端修剪一个字符,并且不会从一端修剪:

    public static string TrimMatchingQuotes(this string input, char quote)
    {
        if ((input.Length >= 2) && 
            (input[0] == quote) && (input[input.Length - 1] == quote))
            return input.Substring(1, input.Length - 2);

        return input;
    }

我想你还需要一些测试。 那么,好吧。 但这绝对是最后一件事! 首先是一个帮助函数,它将分割的结果与期望的数组内容进行比较:

    public static void Test(string cmdLine, params string[] args)
    {
        string[] split = SplitCommandLine(cmdLine).ToArray();

        Debug.Assert(split.Length == args.Length);

        for (int n = 0; n < split.Length; n++)
            Debug.Assert(split[n] == args[n]);
    }

然后我可以写这样的测试:

        Test("");
        Test("a", "a");
        Test(" abc ", "abc");
        Test("a b ", "a", "b");
        Test("a b "c d"", "a", "b", "c d");

以下是您的要求的测试:

        Test(@"/src:""C:tmpSome FolderSub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam",
             @"/src:""C:tmpSome FolderSub Folder""", @"/users:""abcdefg@hijkl.com""", @"tasks:""SomeTask,Some Other Task""", @"-someParam");

请注意,该实现具有额外的功能,即如果有意义的话,它将删除引号周围的引号(感谢TrimMatchingQuotes函数)。 我相信这是正常的命令行解释的一部分。


Windows命令行解析器的行为与您所说的相同,除非在它之前有未封闭的引用。 我会建议你自己写解析器。 这样的事情可能是:

    static string[] ParseArguments(string commandLine)
    {
        char[] parmChars = commandLine.ToCharArray();
        bool inQuote = false;
        for (int index = 0; index < parmChars.Length; index++)
        {
            if (parmChars[index] == '"')
                inQuote = !inQuote;
            if (!inQuote && parmChars[index] == ' ')
                parmChars[index] = 'n';
        }
        return (new string(parmChars)).Split('n');
    }
链接地址: http://www.djcxy.com/p/30415.html

上一篇: line parameters into string[] in C#

下一篇: Suggestions for implementation of a command line interface