स्प्लिट स्ट्रिंग स्ट्रिंग में कमांड लाइन के मापदंडों को [] सी में #


91

मेरे पास एक एकल स्ट्रिंग है जिसमें कमांड-लाइन पैरामीटर को किसी अन्य निष्पादन योग्य में पारित किया जाना है और मुझे स्ट्रिंग को निकालने की आवश्यकता है [] जिसमें व्यक्तिगत पैरामीटर उसी तरह से हैं जिसमें C # होगा यदि कमांड लाइन पर कमांड निर्दिष्ट किए गए थे। स्ट्रिंग [] का उपयोग प्रतिबिंब के माध्यम से एक और असेंबली एंट्री-पॉइंट को निष्पादित करते समय किया जाएगा।

क्या इसके लिए कोई मानक कार्य है? या क्या मापदंडों को सही ढंग से विभाजित करने के लिए एक पसंदीदा तरीका (रेगेक्स?) है? इसे "" सीमांकित स्ट्रिंग्स को संभालना चाहिए, जिसमें रिक्त स्थान सही रूप से शामिल हो सकते हैं, इसलिए मैं बस '' पर विभाजित नहीं कर सकता।

उदाहरण स्ट्रिंग:

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

उदाहरण परिणाम:

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

मुझे लाइब्रेरी में एक कमांड-लाइन पार्सिंग लाइब्रेरी की आवश्यकता नहीं है, बस स्ट्रिंग [] जो उत्पन्न होनी चाहिए।

अपडेट : मुझे अपेक्षित परिणाम को मिलान के लिए बदलना होगा जो वास्तव में C # द्वारा उत्पन्न होता है (विभाजन स्ट्रिंग्स में अतिरिक्त "हटा दिया गया है)



5
हर बार जब कोई जवाब देता है, तो आपको लगता है कि आपके पोस्ट में सामग्री के आधार पर आपत्ति नहीं है। मेरा सुझाव है कि आप इस सामग्री के साथ अपनी पोस्ट को अपडेट करें। आपको बेहतर उत्तर मिल सकते हैं।
tvanfosson

1
अच्छा सवाल, उसी की तलाश में। किसी से यह कहने की उम्मीद कर रहा था कि "हे। नेट यहां उजागर करता है ..." :) अगर मैं किसी बिंदु पर आता हूं, तो मैं इसे यहां पोस्ट करूंगा, भले ही यह 6 साल का हो। फिर भी एक वैध प्रश्न!
माइकजैनसेन

मैंने नीचे एक उत्तर में एक शुद्ध रूप से प्रबंधित संस्करण बनाया है क्योंकि मुझे इस फ़ंक्शन की भी आवश्यकता थी।
ygoe

जवाबों:


75

Earwicker द्वारा अच्छे और शुद्ध प्रबंधित समाधान के अलावा , यह संपूर्णता के लिए, ध्यान देने योग्य हो सकता है, कि विंडोज स्ट्रिंग के एक स्ट्रिंग में एक स्ट्रिंग को तोड़ने के लिए फ़ंक्शन भी प्रदान करता है :CommandLineToArgvW

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

एक यूनिकोड कमांड लाइन स्ट्रिंग को पार्स करता है और कमांड लाइन के तर्कों के साथ पॉइंटर्स की एक सरणी लौटाता है, इस तरह के तर्कों की गिनती के साथ, इस तरह से मानक C रन-टाइम argv और argc मानों के समान होता है।

इस API को C # से कॉल करने और प्रबंधित कोड में परिणामी स्ट्रिंग ऐरे को अनपैक करने का एक उदाहरण मिल सकता है, " CommandLineToArgvW () API का उपयोग करके कमांड लाइन स्ट्रिंग को Args [] में परिवर्तित करना ।" नीचे एक ही कोड का थोड़ा सरल संस्करण है:

[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);
    }
}

1
इस फ़ंक्शन के लिए आवश्यक है कि आप उद्धरण के अंदर किसी पथ के पीछे के भाग से बच जाएं। स्ट्रिंग को सही ढंग से पार्स करने के लिए इसके लिए "C: \ Program Files \" होना चाहिए "C: \ Program Files \\"।
मैग्नस लिंडे

8
यह भी ध्यान देने योग्य है कि CommandLineArgvW प्रोग्राम के नाम होने के पहले तर्क की अपेक्षा करता है, और लागू किया गया जादुई आवेदन एक समान नहीं है यदि कोई पास नहीं हुआ है। आप इसे कुछ इस तरह से नकली बना सकते हैं:CommandLineToArgs("foo.exe " + commandLine).Skip(1).ToArray();
स्कॉट वेगनर

4
पूर्णता के लिए, MSVCRT कमांड लाइन को argc / argv में बदलने के लिए CommandLineToArgvW () का उपयोग नहीं करता है। यह अपने स्वयं के कोड का उपयोग करता है, जो अलग है। उदाहरण के लिए, इस स्ट्रिंग के साथ CreateProcess को कॉल करने का प्रयास करें: "b c" def। मुख्य () में आपको 3 तर्क दिए जाएंगे (जैसा कि MSDN में प्रलेखित है), लेकिन CommandLineToArgvW () / GetCommandLineW () कॉम्बो आपको 2 देगा।
LRN

7
OMG यह एक ऐसी गड़बड़ है। विशिष्ट एमएस सूप। कुछ भी नहीं canonicalized जाता है, और कभी नहीं KISS एमएस दुनिया में सम्मान दिया जाता है।
v.oddou

1
मैंने MSVCRT कार्यान्वयन और Regex का उपयोग करते हुए एक उच्च-सटीकता सन्निकटन Microsoft अनुवादित क्रॉस-प्लेटफ़ॉर्म संस्करण पोस्ट किया। मुझे पता है कि यह पुराना है, लेकिन हे - कोई बॉडी स्क्रॉल नहीं।
TylerY86

101

यह मुझे बताता है कि प्रत्येक फ़ंक्शन की जांच करने वाले फ़ंक्शन के आधार पर स्ट्रिंग को विभाजित करने के लिए कोई फ़ंक्शन नहीं है। अगर वहाँ था, तो आप इसे इस तरह लिख सकते हैं:

    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));
    }

हालांकि लिखा है कि, क्यों नहीं आवश्यक विस्तार के तरीके लिखें। ठीक है, आपने मुझसे इसमें बात की ...

सबसे पहले, स्प्लिट का मेरा अपना संस्करण जो एक फ़ंक्शन लेता है, जिसमें यह तय करना होता है कि निर्दिष्ट वर्ण को स्ट्रिंग को विभाजित करना चाहिए या नहीं:

    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:\tmp\Some Folder\Sub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam",
             @"/src:""C:\tmp\Some Folder\Sub Folder""", @"/users:""abcdefg@hijkl.com""", @"tasks:""SomeTask,Some Other Task""", @"-someParam");

ध्यान दें कि कार्यान्वयन में अतिरिक्त सुविधा है कि यह एक तर्क के आसपास उद्धरणों को हटा देगा अगर यह समझ में आता है (ट्राइमैचिंगक्वाट्स फ़ंक्शन के लिए धन्यवाद)। मेरा मानना ​​है कि यह सामान्य कमांड-लाइन व्याख्या का हिस्सा है।


मुझे इसे उत्तर के रूप में चिह्नित करना पड़ा क्योंकि मेरे पास सही अपेक्षित आउटपुट नहीं थे। वास्तविक उत्पादन अंतिम सरणी में "की नहीं होना चाहिए
एंटोन

16
मैं हर समय बदलने वाली आवश्यकताओं से दूर जाने के लिए स्टैक ओवरफ्लो में आता हूं! :) आप सभी उद्धरणों से छुटकारा पाने के लिए ट्रिममैचिंगक्वाट्स () के बजाय रिप्लेसमेंट ("\" ",") का उपयोग कर सकते हैं। लेकिन एक उद्धरण चरित्र को पारित करने की अनुमति देने के लिए विंडोज \ "का समर्थन करता है। मेरा स्प्लिट फंक्शन ऐसा नहीं कर सकता।
डैनियल इयरविकर

1
नाइस वन ईयरविकर :) एंटोन: यह वह समाधान है जिसे मैं अपनी पिछली पोस्ट में आपको वर्णन करने की कोशिश कर रहा था, लेकिन ईयरविकर ने इसे नीचे लिखने में बहुत बेहतर काम किया;) और इसे बहुत बढ़ा दिया;)
इसरार

व्हाट्सएप केवल कमांड लाइन के तर्कों के लिए अलग चरित्र नहीं है, है ना?
लुईस

@ लुईस राइस - मुझे यकीन नहीं है। अगर यह एक चिंता है, तो इसे हल करना बहुत आसान है: char.IsWhiteSpaceइसके बजाय का उपयोग करें== ' '
डैनियल ईयरविकर

25

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');
    }

2
मैं उसी चीज के साथ समाप्त हुआ, जो मैंने उपयोग किया था। एसप्लिट (नया चार [] {'\ n ’}, स्ट्रिंगरस्प्लिटऑंसर्स।RemoveEmptyEntries) अंतिम पंक्ति में अगर परम के बीच अतिरिक्त थे। काम करने लगता है।
एंटोन

3
मुझे लगता है कि विंडोज में पैरामीटर में उद्धरण से बचने का एक तरीका होना चाहिए ... यह एल्गोरिथ्म इसे ध्यान में नहीं रखता है।
rmeador

खाली पंक्तियों को हटाकर, बाहर के उद्धरणों को हटाकर, भागे हुए उद्धरणों को संभालकर पाठक के लिए एक अंश के रूप में छोड़ दिया जाता है।
जेफरी एल व्हिटलेज 16

Char.IsWhiteSpace () यहां मदद कर सकता है
सैम मैकरिल

यह समाधान अच्छा है यदि तर्क एकल स्थान से अलग हो जाते हैं, लेकिन असफलता तर्क कई स्थानों से अलग हो जाते हैं। सही समाधान के लिए लिंक: stackoverflow.com/a/59131568/3926504
दिलीप नन्नवरे

13

मैंने जेफरी एल व्हाइटलेज से जवाब लिया और इसे थोड़ा बढ़ाया।

अब यह सिंगल और डबल दोनों कोट्स को सपोर्ट करता है। आप अन्य टाइप किए गए उद्धरणों का उपयोग करके मापदंडों में स्वयं उद्धरण का उपयोग कर सकते हैं।

यह तर्कों से उद्धरण भी निकालता है क्योंकि ये तर्क जानकारी में योगदान नहीं करते हैं।

    public static string[] SplitArguments(string commandLine)
    {
        var parmChars = commandLine.ToCharArray();
        var inSingleQuote = false;
        var inDoubleQuote = false;
        for (var index = 0; index < parmChars.Length; index++)
        {
            if (parmChars[index] == '"' && !inSingleQuote)
            {
                inDoubleQuote = !inDoubleQuote;
                parmChars[index] = '\n';
            }
            if (parmChars[index] == '\'' && !inDoubleQuote)
            {
                inSingleQuote = !inSingleQuote;
                parmChars[index] = '\n';
            }
            if (!inSingleQuote && !inDoubleQuote && parmChars[index] == ' ')
                parmChars[index] = '\n';
        }
        return (new string(parmChars)).Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
    }

7

अच्छा और शुद्ध प्रबंधित समाधान द्वारा Earwicker इस तरह संभाल तर्क करने में विफल रहा:

Test("\"He whispered to her \\\"I love you\\\".\"", "He whispered to her \"I love you\".");

इसने 3 तत्व लौटाए:

"He whispered to her \"I
love
you\"."

तो यहाँ "उद्धृत \" बच \ "उद्धरण का समर्थन करने के लिए एक तय है:

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

    return commandLine.Split(c => {
        if (c == '\\' && !isEscaping) { isEscaping = true; return false; }

        if (c == '\"' && !isEscaping)
            inQuotes = !inQuotes;

        isEscaping = false;

        return !inQuotes && Char.IsWhiteSpace(c)/*c == ' '*/;
        })
        .Select(arg => arg.Trim().TrimMatchingQuotes('\"').Replace("\\\"", "\""))
        .Where(arg => !string.IsNullOrEmpty(arg));
}

2 अतिरिक्त मामलों के साथ परीक्षण किया गया:

Test("\"C:\\Program Files\"", "C:\\Program Files");
Test("\"He whispered to her \\\"I love you\\\".\"", "He whispered to her \"I love you\".");

यह भी कहा कि आतिफ अजीज द्वारा कमांडलाइनाइनोअर्गव का उपयोग करने वाला स्वीकृत उत्तर भी विफल रहा। इसने 4 तत्व लौटाए:

He whispered to her \ 
I 
love 
you". 

आशा है कि यह किसी को भविष्य में इस तरह के समाधान की तलाश में मदद करता है।


3
नेक्रोमेंसी के लिए क्षमा करें, लेकिन यह समाधान अभी भी उन चीज़ों को याद करता है, bla.exe aAAA"b\"ASDS\"c"dSADSDजिसके परिणामस्वरूप aAAAb"ASDS"cdSADSDयह समाधान आउटपुट होता है aAAA"b"ASDS"c"dSADSD। मैं बदलने पर विचार हो सकता है TrimMatchingQuotesएक करने के लिए Regex("(?<!\\\\)\\\"")और इसका इस्तेमाल इस तरह
स्किस

4

2
उपयोगी - लेकिन यह आपको केवल वर्तमान प्रक्रिया के लिए भेजे गए कमांड लाइन आर्ग्स प्राप्त करेगा। आवश्यकता एक स्ट्रिंग [] एक स्ट्रिंग से प्राप्त करने की थी "उसी तरह कि सी # अगर कमांड लाइन पर कमांड निर्दिष्ट किया गया था"। मुझे लगता है कि हम यह देखने के लिए एक
डिकम्प्रेसर का

जैसा कि जॉन गैलोवे ने भी पाया ( weblogs.asp.net/jgalloway/archive/2006/09/13/… )) एक डिकंपाइलर ज्यादा मदद नहीं करता है, जो हमें आतिफ के जवाब में वापस लाता है ( stackoverflow.com/questions/293030/… )
रोहनक्रैग

4

मैं iterators की तरह है, और आजकल LINQ बनाता है IEnumerable<String>के रूप में आसानी स्ट्रिंग की सरणियों के रूप में प्रयोग करने योग्य है, तो मेरी ले की भावना निम्नलिखित जेफरी एल Whitledge के जवाब (करने के लिए एक विस्तार पद्धति के रूप में है string):

public static IEnumerable<string> ParseArguments(this string commandLine)
{
    if (string.IsNullOrWhiteSpace(commandLine))
        yield break;

    var sb = new StringBuilder();
    bool inQuote = false;
    foreach (char c in commandLine) {
        if (c == '"' && !inQuote) {
            inQuote = true;
            continue;
        }

        if (c != '"' && !(char.IsWhiteSpace(c) && !inQuote)) {
            sb.Append(c);
            continue;
        }

        if (sb.Length > 0) {
            var result = sb.ToString();
            sb.Clear();
            inQuote = false;
            yield return result;
        }
    }

    if (sb.Length > 0)
        yield return sb.ToString();
}

3

आपके प्रश्न में आपने एक रेगेक्स के लिए कहा, और मैं उनका बहुत बड़ा प्रशंसक और उपयोगकर्ता हूं, इसलिए जब मुझे आपके समान ही इस तर्क को विभाजित करने की आवश्यकता थी, तो मैंने चारों ओर गुगली करने के बाद और सरल समाधान नहीं ढूंढने के बाद अपना रेगेक्स लिखा। मुझे लघु समाधान पसंद हैं, इसलिए मैंने एक बनाया और यहाँ यह है:

            var re = @"\G(""((""""|[^""])+)""|(\S+)) *";
            var ms = Regex.Matches(CmdLine, re);
            var list = ms.Cast<Match>()
                         .Select(m => Regex.Replace(
                             m.Groups[2].Success
                                 ? m.Groups[2].Value
                                 : m.Groups[4].Value, @"""""", @"""")).ToArray();

यह उद्धरण चिह्नों के अंदर रिक्त और उद्धरणों को संभालता है, और संलग्न "" को "" में परिवर्तित करता है। कोड का उपयोग करने के लिए स्वतंत्र महसूस करें!


3

ओह बिल्ली। यह सब ... Eugh है। लेकिन यह कानूनी अधिकारी है। .NET कोर के लिए Microsoft से #, शायद विंडोज़ केवल, शायद क्रॉस-प्लेटफ़ॉर्म, लेकिन एमआईटी लाइसेंस प्राप्त है।

Tidbits, विधि घोषणाओं और उल्लेखनीय टिप्पणियों का चयन करें;

internal static unsafe string[] InternalCreateCommandLine(bool includeArg0)
private static unsafe int SegmentCommandLine(char * pCmdLine, string[] argArray, bool includeArg0)
private static unsafe int ScanArgument0(ref char* psrc, char[] arg)
private static unsafe int ScanArgument(ref char* psrc, ref bool inquote, char[] arg)

-

// First, parse the program name (argv[0]). Argv[0] is parsed under special rules. Anything up to 
// the first whitespace outside a quoted subtring is accepted. Backslashes are treated as normal 
// characters.

-

// Rules: 2N backslashes + " ==> N backslashes and begin/end quote
//      2N+1 backslashes + " ==> N backslashes + literal "
//         N backslashes     ==> N backslashes

यह .NET फ्रेमवर्क से .NET कोर के लिए कोडित पोर्ट है जो मुझे लगता है कि MSVC C लाइब्रेरी है या CommandLineToArgvW

यहाँ नियमित अभिव्यक्तियों के साथ कुछ shenanigans को संभालने का मेरा आधा-अधूरा प्रयास है, और तर्क को शून्य सा अनदेखा कर रहा है। यह थोड़ा जादूगर है।

private static readonly Regex RxWinArgs
  = new Regex("([^\\s\"]+\"|((?<=\\s|^)(?!\"\"(?!\"))\")+)(\"\"|.*?)*\"[^\\s\"]*|[^\\s]+",
    RegexOptions.Compiled
    | RegexOptions.Singleline
    | RegexOptions.ExplicitCapture
    | RegexOptions.CultureInvariant);

internal static IEnumerable<string> ParseArgumentsWindows(string args) {
  var match = RxWinArgs.Match(args);

  while (match.Success) {
    yield return match.Value;
    match = match.NextMatch();
  }
}

यह निराला उत्पन्न उत्पादन पर एक निष्पक्ष सा परीक्षण किया। यह उत्पादन बंदरों के टाइप किए गए और भाग जाने के उचित प्रतिशत से मेल खाता है CommandLineToArgvW



1
हाँ ऐसा लगता है जैसे C # संस्करण मृत है। github.com/dotnet/runtime/blob/master/src/coreclr/src/utilcode/…
TylerY86

1
सीमित समय पुनरुद्धार। pastebin.com/ajhrBS4t
TylerY86

2

यह कोड प्रोजेक्ट आलेख वह है जो मैंने अतीत में उपयोग किया है। यह एक अच्छा कोड है, लेकिन यह काम कर सकता है।

यह एमएसडीएन लेख केवल एक चीज है जो मुझे मिल सकता है जो बताता है कि कैसे सी # पर्स कमांड लाइन तर्क देता है।


मैंने C # लाइब्रेरी में रिफ्लेक्टर की कोशिश की, लेकिन यह एक देशी C ++ कॉल के लिए जाता है, जिसके लिए मेरे पास कोड नहीं है, और इसे पी-इनवॉइस किए बिना कॉल करने का कोई तरीका नहीं देख सकता। मैं भी एक कमांड-लाइन पार्सिंग लाइब्रेरी नहीं चाहता, मुझे बस स्ट्रिंग चाहिए []।
एंटोन

Reflecting .NET ने मुझे भी कहीं नहीं लाया। मोनो स्रोत कोड को देखते हुए सुझाव दिया गया कि यह तर्क विभाजन सीएलआर द्वारा नहीं किया गया है, बल्कि पहले से ही ऑपरेटिंग सिस्टम से आता है। C मुख्य फ़ंक्शन के argc, argv पैरामीटर के बारे में सोचें। तो ओएस एपीआई के अलावा अन्य कुछ भी पुन: उपयोग करने के लिए नहीं है।
ygoe

1

एक विशुद्ध रूप से प्रबंधित समाधान सहायक हो सकता है। WinAPI फ़ंक्शन के लिए बहुत अधिक "समस्या" टिप्पणियां हैं और यह अन्य प्लेटफार्मों पर उपलब्ध नहीं है। यहाँ मेरा कोड है जिसमें एक अच्छी तरह से परिभाषित व्यवहार है (यदि आप चाहें तो बदल सकते हैं)।

यह उस string[] argsपैरामीटर को प्रदान करते समय .NET / Windows जैसा करता है, वैसा ही करना चाहिए , और मैंने इसकी तुलना "दिलचस्प" मानों की संख्या के साथ की है।

यह एक क्लासिक स्टेट-मशीन कार्यान्वयन है जो इनपुट स्ट्रिंग से प्रत्येक एकल चरित्र को लेता है और वर्तमान स्थिति, उत्पादन और एक नए राज्य के लिए व्याख्या करता है। राज्य चर में निर्धारित है escape, inQuote, hadQuoteऔर prevCh, और उत्पादन में एकत्र किया जाता है currentArgऔर args

कुछ खासियतें जिन्हें मैंने वास्तविक कमांड प्रॉम्प्ट (विंडोज 7) पर प्रयोगों द्वारा खोजा है: एक उद्धृत सीमा के भीतर \\उत्पादन \, \"उत्पादन ", ""पैदा करता है "

^चरित्र, जादुई प्रतीत हो रहा है बहुत: यह हमेशा गायब हो जाता है जब यह दोहरीकरण नहीं। अन्यथा इसका वास्तविक कमांड लाइन पर कोई प्रभाव नहीं पड़ता है। मेरा कार्यान्वयन इसका समर्थन नहीं करता है, क्योंकि मुझे इस व्यवहार में कोई पैटर्न नहीं मिला है। शायद किसी को इसके बारे में अधिक पता हो।

इस पैटर्न में जो कुछ फिट नहीं होता है वह निम्न कमांड है:

cmd /c "argdump.exe "a b c""

cmdआदेश बाहरी उद्धरण को पकड़ने और बाकी शब्दशः लेने के लिए लगता है। इसमें कुछ विशेष मैजिक सॉस होना चाहिए।

मैंने अपने तरीके पर कोई बेंचमार्क नहीं किया है, लेकिन इसे काफी तेजी से समझें। इसका उपयोग नहीं किया जाता है Regexऔर यह किसी भी स्ट्रिंग संघनन नहीं करता है, बल्कि StringBuilderएक तर्क के लिए पात्रों को इकट्ठा करने और उन्हें एक सूची में रखने के लिए उपयोग करता है।

/// <summary>
/// Reads command line arguments from a single string.
/// </summary>
/// <param name="argsString">The string that contains the entire command line.</param>
/// <returns>An array of the parsed arguments.</returns>
public string[] ReadArgs(string argsString)
{
    // Collects the split argument strings
    List<string> args = new List<string>();
    // Builds the current argument
    var currentArg = new StringBuilder();
    // Indicates whether the last character was a backslash escape character
    bool escape = false;
    // Indicates whether we're in a quoted range
    bool inQuote = false;
    // Indicates whether there were quotes in the current arguments
    bool hadQuote = false;
    // Remembers the previous character
    char prevCh = '\0';
    // Iterate all characters from the input string
    for (int i = 0; i < argsString.Length; i++)
    {
        char ch = argsString[i];
        if (ch == '\\' && !escape)
        {
            // Beginning of a backslash-escape sequence
            escape = true;
        }
        else if (ch == '\\' && escape)
        {
            // Double backslash, keep one
            currentArg.Append(ch);
            escape = false;
        }
        else if (ch == '"' && !escape)
        {
            // Toggle quoted range
            inQuote = !inQuote;
            hadQuote = true;
            if (inQuote && prevCh == '"')
            {
                // Doubled quote within a quoted range is like escaping
                currentArg.Append(ch);
            }
        }
        else if (ch == '"' && escape)
        {
            // Backslash-escaped quote, keep it
            currentArg.Append(ch);
            escape = false;
        }
        else if (char.IsWhiteSpace(ch) && !inQuote)
        {
            if (escape)
            {
                // Add pending escape char
                currentArg.Append('\\');
                escape = false;
            }
            // Accept empty arguments only if they are quoted
            if (currentArg.Length > 0 || hadQuote)
            {
                args.Add(currentArg.ToString());
            }
            // Reset for next argument
            currentArg.Clear();
            hadQuote = false;
        }
        else
        {
            if (escape)
            {
                // Add pending escape char
                currentArg.Append('\\');
                escape = false;
            }
            // Copy character from input, no special meaning
            currentArg.Append(ch);
        }
        prevCh = ch;
    }
    // Save last argument
    if (currentArg.Length > 0 || hadQuote)
    {
        args.Add(currentArg.ToString());
    }
    return args.ToArray();
}

1

उपयोग:

public static string[] SplitArguments(string args) {
    char[] parmChars = args.ToCharArray();
    bool inSingleQuote = false;
    bool inDoubleQuote = false;
    bool escaped = false;
    bool lastSplitted = false;
    bool justSplitted = false;
    bool lastQuoted = false;
    bool justQuoted = false;

    int i, j;

    for(i=0, j=0; i<parmChars.Length; i++, j++) {
        parmChars[j] = parmChars[i];

        if(!escaped) {
            if(parmChars[i] == '^') {
                escaped = true;
                j--;
            } else if(parmChars[i] == '"' && !inSingleQuote) {
                inDoubleQuote = !inDoubleQuote;
                parmChars[j] = '\n';
                justSplitted = true;
                justQuoted = true;
            } else if(parmChars[i] == '\'' && !inDoubleQuote) {
                inSingleQuote = !inSingleQuote;
                parmChars[j] = '\n';
                justSplitted = true;
                justQuoted = true;
            } else if(!inSingleQuote && !inDoubleQuote && parmChars[i] == ' ') {
                parmChars[j] = '\n';
                justSplitted = true;
            }

            if(justSplitted && lastSplitted && (!lastQuoted || !justQuoted))
                j--;

            lastSplitted = justSplitted;
            justSplitted = false;

            lastQuoted = justQuoted;
            justQuoted = false;
        } else {
            escaped = false;
        }
    }

    if(lastQuoted)
        j--;

    return (new string(parmChars, 0, j)).Split(new[] { '\n' });
}

एले के उत्तर में वाष्प के आधार पर , यह भी ^ एस्केप का समर्थन करता है।

उदाहरण:

  • यह एक परीक्षण है
    • यह
    • है
    • परीक्षा
  • यह एक परीक्षण है
    • यह
    • एक है
    • परीक्षा
  • यह ^ "एक ^" परीक्षा है
    • यह
    • "है
    • ए"
    • परीक्षा
  • यह "" "^ ^ ^ टेस्ट है"
    • यह
    • एक ^ परीक्षा है

यह कई स्थानों का भी समर्थन करता है (रिक्त स्थान के प्रति ब्लॉक केवल एक बार तर्क को तोड़ता है)।


तीनों में से अंतिम किसी भी तरह मार्कडाउन के साथ हस्तक्षेप करता है और इसका इरादा नहीं है।
पीटर मोर्टेंसन

शून्य-चौड़ाई-स्थान के साथ निश्चित।
Fabio Iotti

1

क्योंकि मुझे ओपी के समान व्यवहार चाहिए था (एक स्ट्रिंग को बिल्कुल उसी तरह विभाजित करें जैसे कि विंडोज़ cmd करेगा) मैंने परीक्षण मामलों का एक गुच्छा लिखा और यहां पोस्ट किए गए उत्तरों का परीक्षण किया:

    Test( 0, m, "One",                    new[] { "One" });
    Test( 1, m, "One ",                   new[] { "One" });
    Test( 2, m, " One",                   new[] { "One" });
    Test( 3, m, " One ",                  new[] { "One" });
    Test( 4, m, "One Two",                new[] { "One", "Two" });
    Test( 5, m, "One  Two",               new[] { "One", "Two" });
    Test( 6, m, "One   Two",              new[] { "One", "Two" });
    Test( 7, m, "\"One Two\"",            new[] { "One Two" });
    Test( 8, m, "One \"Two Three\"",      new[] { "One", "Two Three" });
    Test( 9, m, "One \"Two Three\" Four", new[] { "One", "Two Three", "Four" });
    Test(10, m, "One=\"Two Three\" Four", new[] { "One=Two Three", "Four" });
    Test(11, m, "One\"Two Three\" Four",  new[] { "OneTwo Three", "Four" });
    Test(12, m, "One\"Two Three   Four",  new[] { "OneTwo Three   Four" });
    Test(13, m, "\"One Two\"",            new[] { "One Two" });
    Test(14, m, "One\" \"Two",            new[] { "One Two" });
    Test(15, m, "\"One\"  \"Two\"",       new[] { "One", "Two" });
    Test(16, m, "One\\\"  Two",           new[] { "One\"", "Two" });
    Test(17, m, "\\\"One\\\"  Two",       new[] { "\"One\"", "Two" });
    Test(18, m, "One\"",                  new[] { "One" });
    Test(19, m, "\"One",                  new[] { "One" });
    Test(20, m, "One \"\"",               new[] { "One", "" });
    Test(21, m, "One \"",                 new[] { "One", "" });
    Test(22, m, "1 A=\"B C\"=D 2",        new[] { "1", "A=B C=D", "2" });
    Test(23, m, "1 A=\"B \\\" C\"=D 2",   new[] { "1", "A=B \" C=D", "2" });
    Test(24, m, "1 \\A 2",                new[] { "1", "\\A", "2" });
    Test(25, m, "1 \\\" 2",               new[] { "1", "\"", "2" });
    Test(26, m, "1 \\\\\" 2",             new[] { "1", "\\\"", "2" });
    Test(27, m, "\"",                     new[] { "" });
    Test(28, m, "\\\"",                   new[] { "\"" });
    Test(29, m, "'A B'",                  new[] { "'A", "B'" });
    Test(30, m, "^",                      new[] { "^" });
    Test(31, m, "^A",                     new[] { "A" });
    Test(32, m, "^^",                     new[] { "^" });
    Test(33, m, "\\^^",                   new[] { "\\^" });
    Test(34, m, "^\\\\", new[] { "\\\\" });
    Test(35, m, "^\"A B\"", new[] { "A B" });

    // Test cases Anton

    Test(36, m, @"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam foo", new[] { @"/src:C:\tmp\Some Folder\Sub Folder", @"/users:abcdefg@hijkl.com", @"tasks:SomeTask,Some Other Task", @"-someParam", @"foo" });

    // Test cases Daniel Earwicker 

    Test(37, m, "", new string[] { });
    Test(38, m, "a", new[] { "a" });
    Test(39, m, " abc ", new[] { "abc" });
    Test(40, m, "a b ", new[] { "a", "b" });
    Test(41, m, "a b \"c d\"", new[] { "a", "b", "c d" });

    // Test cases Fabio Iotti 

    Test(42, m, "this is a test ", new[] { "this", "is", "a", "test" });
    Test(43, m, "this \"is a\" test", new[] { "this", "is a", "test" });

    // Test cases Kevin Thach

    Test(44, m, "\"C:\\Program Files\"", new[] { "C:\\Program Files" });
    Test(45, m, "\"He whispered to her \\\"I love you\\\".\"", new[] { "He whispered to her \"I love you\"." });

"अपेक्षित" मान सीधे मेरी मशीन (Win10 x64) और एक साधारण प्रिंट प्रोग्राम पर cmd.exe के साथ परीक्षण करने से आता है:

static void Main(string[] args) => Console.Out.WriteLine($"Count := {args.Length}\n{string.Join("\n", args.Select((v,i) => $"[{i}] => '{v}'"))}");

ये परिणाम हैं:


Solution                      | Failed Tests
------------------------------|------------------------------------- 
Atif Aziz (749653)            | 2, 3, 10, 11, 12, 14, 16, 17, 18, 26, 28, 31, 32, 33, 34, 35, 36, 37, 39, 45
Jeffrey L Whitledge (298968)  | 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45
Daniel Earwicker (298990)     | 10, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 45
Anton (299795)                | 12, 16, 17, 18, 19, 21, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 45
CS. (467313)                  | 12, 18, 19, 21, 27, 31, 32, 33, 34, 35
Vapour in the Alley (2132004) | 10, 11, 12, 14, 16, 17, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 45
Monoman (7774211)             | 14, 16, 17, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 45
Thomas Petersson (19091999)   | 2, 3, 10, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 39, 45
Fabio Iotti (19725880)        | 1, 2, 3, 7, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 22, 23, 25, 26, 28, 29, 30, 35, 36, 37, 39, 40, 42, 44, 45
ygoe (23961658)               | 26, 31, 32, 33, 34, 35
Kevin Thach (24829691)        | 10, 11, 12, 14, 18, 19, 20, 21, 22, 23, 26, 27, 31, 32, 33, 34, 35, 36
Lucas De Jesus (31621370)     | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45
HarryP (48008872)             | 24, 26, 31, 32, 33, 34, 35
TylerY86 (53290784)           | 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 41, 43, 44, 45
Louis Somers (55903304)       | 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 39, 41, 43, 44, 45
user2126375 (58233585)        | 5, 6, 15, 16, 17, 31, 32, 33, 34, 35
DilipNannaware (59131568)     | 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45
Mikescher (this)              | -

क्योंकि कोई भी उत्तर सही नहीं लगता था (कम से कम मेरे उपयोग के मामले पर आधारित) यहाँ मेरा समाधान है, यह वर्तमान में सभी परीक्षण मामलों से गुजरता है (लेकिन यदि किसी के पास अतिरिक्त (असफल) कोने के मामले हैं तो कृपया टिप्पणी करें):

public static IEnumerable<string> SplitArgs(string commandLine)
{
    var result = new StringBuilder();

    var quoted = false;
    var escaped = false;
    var started = false;
    var allowcaret = false;
    for (int i = 0; i < commandLine.Length; i++)
    {
        var chr = commandLine[i];

        if (chr == '^' && !quoted)
        {
            if (allowcaret)
            {
                result.Append(chr);
                started = true;
                escaped = false;
                allowcaret = false;
            }
            else if (i + 1 < commandLine.Length && commandLine[i + 1] == '^')
            {
                allowcaret = true;
            }
            else if (i + 1 == commandLine.Length)
            {
                result.Append(chr);
                started = true;
                escaped = false;
            }
        }
        else if (escaped)
        {
            result.Append(chr);
            started = true;
            escaped = false;
        }
        else if (chr == '"')
        {
            quoted = !quoted;
            started = true;
        }
        else if (chr == '\\' && i + 1 < commandLine.Length && commandLine[i + 1] == '"')
        {
            escaped = true;
        }
        else if (chr == ' ' && !quoted)
        {
            if (started) yield return result.ToString();
            result.Clear();
            started = false;
        }
        else
        {
            result.Append(chr);
            started = true;
        }
    }

    if (started) yield return result.ToString();
}

परीक्षण परिणामों को उत्पन्न करने के लिए मैंने जो कोड इस्तेमाल किया था, वह यहां पाया जा सकता है


0

वर्तमान में, यह वह कोड है जो मेरे पास है:

    private String[] SplitCommandLineArgument(String argumentString)
    {
        StringBuilder translatedArguments = new StringBuilder(argumentString);
        bool escaped = false;
        for (int i = 0; i < translatedArguments.Length; i++)
        {
            if (translatedArguments[i] == '"')
            {
                escaped = !escaped;
            }
            if (translatedArguments[i] == ' ' && !escaped)
            {
                translatedArguments[i] = '\n';
            }
        }

        string[] toReturn = translatedArguments.ToString().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
        for(int i = 0; i < toReturn.Length; i++)
        {
            toReturn[i] = RemoveMatchingQuotes(toReturn[i]);
        }
        return toReturn;
    }

    public static string RemoveMatchingQuotes(string stringToTrim)
    {
        int firstQuoteIndex = stringToTrim.IndexOf('"');
        int lastQuoteIndex = stringToTrim.LastIndexOf('"');
        while (firstQuoteIndex != lastQuoteIndex)
        {
            stringToTrim = stringToTrim.Remove(firstQuoteIndex, 1);
            stringToTrim = stringToTrim.Remove(lastQuoteIndex - 1, 1); //-1 because we've shifted the indicies left by one
            firstQuoteIndex = stringToTrim.IndexOf('"');
            lastQuoteIndex = stringToTrim.LastIndexOf('"');
        }
        return stringToTrim;
    }

यह भागे हुए उद्धरणों के साथ काम नहीं करता है, लेकिन यह उन मामलों के लिए काम करता है जो मैं अब तक के खिलाफ आया हूं।


0

यह एंटोन के कोड का जवाब है, जो बच गए उद्धरणों के साथ काम नहीं करता है। मैंने 3 स्थानों को संशोधित किया।

  1. निर्माता के लिए StringBuilder में SplitCommandLineArguments , किसी भी जगह \ " के साथ \ r
  2. में के लिए लूप में SplitCommandLineArguments , मैं अब की जगह \ r करने के लिए चरित्र वापस "\
  3. SplitCommandLineArgument विधि को निजी से सार्वजनिक स्थैतिक में बदल दिया ।

public static string[] SplitCommandLineArgument( String argumentString )
{
    StringBuilder translatedArguments = new StringBuilder( argumentString ).Replace( "\\\"", "\r" );
    bool InsideQuote = false;
    for ( int i = 0; i < translatedArguments.Length; i++ )
    {
        if ( translatedArguments[i] == '"' )
        {
            InsideQuote = !InsideQuote;
        }
        if ( translatedArguments[i] == ' ' && !InsideQuote )
        {
            translatedArguments[i] = '\n';
        }
    }

    string[] toReturn = translatedArguments.ToString().Split( new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries );
    for ( int i = 0; i < toReturn.Length; i++ )
    {
        toReturn[i] = RemoveMatchingQuotes( toReturn[i] );
        toReturn[i] = toReturn[i].Replace( "\r", "\"" );
    }
    return toReturn;
}

public static string RemoveMatchingQuotes( string stringToTrim )
{
    int firstQuoteIndex = stringToTrim.IndexOf( '"' );
    int lastQuoteIndex = stringToTrim.LastIndexOf( '"' );
    while ( firstQuoteIndex != lastQuoteIndex )
    {
        stringToTrim = stringToTrim.Remove( firstQuoteIndex, 1 );
        stringToTrim = stringToTrim.Remove( lastQuoteIndex - 1, 1 ); //-1 because we've shifted the indicies left by one
        firstQuoteIndex = stringToTrim.IndexOf( '"' );
        lastQuoteIndex = stringToTrim.LastIndexOf( '"' );
    }
    return stringToTrim;
}

मैं इसी मुद्दे से निपट रहा हूं, आपने सोचा होगा कि इस दिन और उम्र में यूनिट परीक्षण कमांड लाइन तर्क तार के लिए एक सरल समाधान मौजूद होगा। मैं सभी को सुनिश्चित करना चाहता हूं कि वह व्यवहार है जो किसी दिए गए कमांडलाइन तर्क स्ट्रिंग से उत्पन्न होगा। मैं अभी के लिए दे रहा हूं और स्ट्रिंग के लिए यूनिट परीक्षण बनाऊंगा [] लेकिन इसे बंद करने के लिए कुछ एकीकरण परीक्षण जोड़ सकता हूं।
चार्ली बार्कर

0

मुझे नहीं लगता कि C # एप्लिकेशन के लिए एकल उद्धरण या ^ उद्धरण हैं। निम्न कार्य मेरे लिए ठीक काम कर रहा है:

public static IEnumerable<String> SplitArguments(string commandLine)
{
    Char quoteChar = '"';
    Char escapeChar = '\\';
    Boolean insideQuote = false;
    Boolean insideEscape = false;

    StringBuilder currentArg = new StringBuilder();

    // needed to keep "" as argument but drop whitespaces between arguments
    Int32 currentArgCharCount = 0;                  

    for (Int32 i = 0; i < commandLine.Length; i++)
    {
        Char c = commandLine[i];
        if (c == quoteChar)
        {
            currentArgCharCount++;

            if (insideEscape)
            {
                currentArg.Append(c);       // found \" -> add " to arg
                insideEscape = false;
            }
            else if (insideQuote)
            {
                insideQuote = false;        // quote ended
            }
            else
            {
                insideQuote = true;         // quote started
            }
        }
        else if (c == escapeChar)
        {
            currentArgCharCount++;

            if (insideEscape)   // found \\ -> add \\ (only \" will be ")
                currentArg.Append(escapeChar + escapeChar);       

            insideEscape = !insideEscape;
        }
        else if (Char.IsWhiteSpace(c))
        {
            if (insideQuote)
            {
                currentArgCharCount++;
                currentArg.Append(c);       // append whitespace inside quote
            }
            else
            {
                if (currentArgCharCount > 0)
                    yield return currentArg.ToString();

                currentArgCharCount = 0;
                currentArg.Clear();
            }
        }
        else
        {
            currentArgCharCount++;
            if (insideEscape)
            {
                // found non-escaping backslash -> add \ (only \" will be ")
                currentArg.Append(escapeChar);                       
                currentArgCharCount = 0;
                insideEscape = false;
            }
            currentArg.Append(c);
        }
    }

    if (currentArgCharCount > 0)
        yield return currentArg.ToString();
}

0

आपके द्वारा कल पोस्ट किए गए कोड पर एक नज़र डाल सकते हैं:

[C #] पथ और तर्क तार

यह स्ट्रिंग में एक फ़ाइल नाम + तर्क विभाजित करता है []। छोटे रास्ते, पर्यावरण चर और गुम फ़ाइल एक्सटेंशन को संभाला जाता है।

(शुरुआत में यह रजिस्ट्री में अनइंस्टॉलिंग के लिए था।)


0

इस कोड को आज़माएं:

    string[] str_para_linha_comando(string str, out int argumentos)
    {
        string[] linhaComando = new string[32];
        bool entre_aspas = false;
        int posicao_ponteiro = 0;
        int argc = 0;
        int inicio = 0;
        int fim = 0;
        string sub;

        for(int i = 0; i < str.Length;)
        {
            if (entre_aspas)
            {
                // Está entre aspas
                sub = str.Substring(inicio+1, fim - (inicio+1));
                linhaComando[argc - 1] = sub;

                posicao_ponteiro += ((fim - posicao_ponteiro)+1);
                entre_aspas = false;
                i = posicao_ponteiro;
            }
            else
            {
            tratar_aspas:
                if (str.ElementAt(i) == '\"')
                {
                    inicio = i;
                    fim = str.IndexOf('\"', inicio + 1);
                    entre_aspas = true;
                    argc++;
                }
                else
                {
                    // Se não for aspas, então ler até achar o primeiro espaço em branco
                    if (str.ElementAt(i) == ' ')
                    {
                        if (str.ElementAt(i + 1) == '\"')
                        {
                            i++;
                            goto tratar_aspas;
                        }

                        // Pular os espaços em branco adiconais
                        while(str.ElementAt(i) == ' ') i++;

                        argc++;
                        inicio = i;
                        fim = str.IndexOf(' ', inicio);
                        if (fim == -1) fim = str.Length;
                        sub = str.Substring(inicio, fim - inicio);
                        linhaComando[argc - 1] = sub;
                        posicao_ponteiro += (fim - posicao_ponteiro);

                        i = posicao_ponteiro;
                        if (posicao_ponteiro == str.Length) break;
                    }
                    else
                    {
                        argc++;
                        inicio = i;
                        fim = str.IndexOf(' ', inicio);
                        if (fim == -1) fim = str.Length;

                        sub = str.Substring(inicio, fim - inicio);
                        linhaComando[argc - 1] = sub;
                        posicao_ponteiro += fim - posicao_ponteiro;
                        i = posicao_ponteiro;
                        if (posicao_ponteiro == str.Length) break;
                    }
                }
            }
        }

        argumentos = argc;

        return linhaComando;
    }

यह पुर्तगाली में लिखा गया है।


बल्कि प्रलेखन है
Enamul Hassan

@EnamulHassan मैं कहूंगा कि कोड पुर्तगाली में भी है, उदा posicao_ponteiro += ((fim - posicao_ponteiro)+1);
मेमोरियल

0

यहाँ एक लाइनर है जो काम करवाता है (एक लाइन देखें जो बर्स्टकैमलीनएरग्स (...) विधि के अंदर सभी काम करता है)।

मैं कोड की सबसे पठनीय लाइन नहीं कहूंगा, लेकिन आप इसे पठनीयता के लिए तोड़ सकते हैं। यह उद्देश्य पर सरल है और सभी तर्क मामलों के लिए अच्छी तरह से काम नहीं करता है (जैसे फ़ाइल नाम तर्क जिसमें विभाजित स्ट्रिंग चरित्र सीमांकक होते हैं)।

इस समाधान ने मेरे समाधानों में अच्छी तरह से काम किया है जो इसका उपयोग करते हैं। जैसा कि मैंने कहा, यह हर संभव तर्क प्रारूप n-factorial को संभालने के लिए चूहे के घोंसले के कोड के बिना काम करता है।

using System;
using System.Collections.Generic;
using System.Linq;

namespace CmdArgProcessor
{
    class Program
    {
        static void Main(string[] args)
        {
            // test switches and switches with values
            // -test1 1 -test2 2 -test3 -test4 -test5 5

            string dummyString = string.Empty;

            var argDict = BurstCmdLineArgs(args);

            Console.WriteLine("Value for switch = -test1: {0}", argDict["test1"]);
            Console.WriteLine("Value for switch = -test2: {0}", argDict["test2"]);
            Console.WriteLine("Switch -test3 is present? {0}", argDict.TryGetValue("test3", out dummyString));
            Console.WriteLine("Switch -test4 is present? {0}", argDict.TryGetValue("test4", out dummyString));
            Console.WriteLine("Value for switch = -test5: {0}", argDict["test5"]);

            // Console output:
            //
            // Value for switch = -test1: 1
            // Value for switch = -test2: 2
            // Switch -test3 is present? True
            // Switch -test4 is present? True
            // Value for switch = -test5: 5
        }

        public static Dictionary<string, string> BurstCmdLineArgs(string[] args)
        {
            var argDict = new Dictionary<string, string>();

            // Flatten the args in to a single string separated by a space.
            // Then split the args on the dash delimiter of a cmd line "switch".
            // E.g. -mySwitch myValue
            //  or -JustMySwitch (no value)
            //  where: all values must follow a switch.
            // Then loop through each string returned by the split operation.
            // If the string can be split again by a space character,
            // then the second string is a value to be paired with a switch,
            // otherwise, only the switch is added as a key with an empty string as the value.
            // Use dictionary indexer to retrieve values for cmd line switches.
            // Use Dictionary::ContainsKey(...) where only a switch is recorded as the key.
            string.Join(" ", args).Split('-').ToList().ForEach(s => argDict.Add(s.Split()[0], (s.Split().Count() > 1 ? s.Split()[1] : "")));

            return argDict;
        }
    }
}

0

यहाँ मुझे पसंद आया कुछ भी नहीं मिला। मैं एक छोटी सी कमांड-लाइन के लिए यील्ड मैजिक के साथ स्टैक को गड़बड़ करने से नफरत करता हूं (यदि यह एक टेराबाइट की एक धारा थी, तो यह एक और कहानी होगी)।

यहाँ मेरा ले रहा है, यह इस तरह से दोहरे उद्धरण के साथ उद्धरण से बचने का समर्थन करता है:

param = "15" "स्क्रीन खराब नहीं है" param2 = '15 "स्क्रीन खराब नहीं है

परिणाम:

परम = "15" स्क्रीन खराब नहीं है "

param2 = '15 'स्क्रीन खराब नहीं है'

param3 = ""

param4 =

/ param5

public static string[] SplitArguments(string commandLine)
{
    List<string> args         = new List<string>();
    List<char>   currentArg   = new List<char>();
    char?        quoteSection = null; // Keeps track of a quoted section (and the type of quote that was used to open it)
    char[]       quoteChars   = new[] {'\'', '\"'};
    char         previous     = ' '; // Used for escaping double quotes

    for (var index = 0; index < commandLine.Length; index++)
    {
        char c = commandLine[index];
        if (quoteChars.Contains(c))
        {
            if (previous == c) // Escape sequence detected
            {
                previous = ' '; // Prevent re-escaping
                if (!quoteSection.HasValue)
                {
                    quoteSection = c; // oops, we ended the quoted section prematurely
                    continue;         // don't add the 2nd quote (un-escape)
                }

                if (quoteSection.Value == c)
                    quoteSection = null; // appears to be an empty string (not an escape sequence)
            }
            else if (quoteSection.HasValue)
            {
                if (quoteSection == c)
                    quoteSection = null; // End quoted section
            }
            else
                quoteSection = c; // Start quoted section
        }
        else if (char.IsWhiteSpace(c))
        {
            if (!quoteSection.HasValue)
            {
                args.Add(new string(currentArg.ToArray()));
                currentArg.Clear();
                previous = c;
                continue;
            }
        }

        currentArg.Add(c);
        previous = c;
    }

    if (currentArg.Count > 0)
        args.Add(new string(currentArg.ToArray()));

    return args.ToArray();
}

0

मैंने राज्य मशीन को उसी पार्सर परिणाम के लिए लागू किया है जैसे कि args को .NET अनुप्रयोग में पारित किया जाएगा और static void Main(string[] args)विधि में संसाधित किया जाएगा ।

    public static IList<string> ParseCommandLineArgsString(string commandLineArgsString)
    {
        List<string> args = new List<string>();

        commandLineArgsString = commandLineArgsString.Trim();
        if (commandLineArgsString.Length == 0)
            return args;

        int index = 0;
        while (index != commandLineArgsString.Length)
        {
            args.Add(ReadOneArgFromCommandLineArgsString(commandLineArgsString, ref index));
        }

        return args;
    }

    private static string ReadOneArgFromCommandLineArgsString(string line, ref int index)
    {
        if (index >= line.Length)
            return string.Empty;

        var sb = new StringBuilder(512);
        int state = 0;
        while (true)
        {
            char c = line[index];
            index++;
            switch (state)
            {
                case 0: //string outside quotation marks
                    if (c == '\\') //possible escaping character for quotation mark otherwise normal character
                    {
                        state = 1;
                    }
                    else if (c == '"') //opening quotation mark for string between quotation marks
                    {
                        state = 2;
                    }
                    else if (c == ' ') //closing arg
                    {
                        return sb.ToString();
                    }
                    else
                    {
                        sb.Append(c);
                    }

                    break;
                case 1: //possible escaping \ for quotation mark or normal character
                    if (c == '"') //If escaping quotation mark only quotation mark is added into result
                    {
                        state = 0;
                        sb.Append(c);
                    }
                    else // \ works as not-special character
                    {
                        state = 0;
                        sb.Append('\\');
                        index--;
                    }

                    break;
                case 2: //string between quotation marks
                    if (c == '"') //quotation mark in string between quotation marks can be escape mark for following quotation mark or can be ending quotation mark for string between quotation marks
                    {
                        state = 3;
                    }
                    else if (c == '\\') //escaping \ for possible following quotation mark otherwise normal character
                    {
                        state = 4;
                    }
                    else //text in quotation marks
                    {
                        sb.Append(c);
                    }

                    break;
                case 3: //quotation mark in string between quotation marks
                    if (c == '"') //Quotation mark after quotation mark - that means that this one is escaped and can added into result and we will stay in string between quotation marks state
                    {
                        state = 2;
                        sb.Append(c);
                    }
                    else //we had two consecutive quotation marks - this means empty string but the following chars (until space) will be part of same arg result as well
                    {
                        state = 0;
                        index--;
                    }

                    break;
                case 4: //possible escaping \ for quotation mark or normal character in string between quotation marks
                    if (c == '"') //If escaping quotation mark only quotation mark added into result
                    {
                        state = 2;
                        sb.Append(c);
                    }
                    else
                    {
                        state = 2;
                        sb.Append('\\');
                        index--;
                    }

                    break;
            }

            if (index == line.Length)
                return sb.ToString();
        }
    }

0

यहाँ समाधान है जो कमांड लाइन पैरामीटर विभाजक के रूप में अंतरिक्ष (एस) (एकल या कई रिक्त स्थान) का इलाज करता है और वास्तविक कमांड लाइन तर्क देता है:

static string[] ParseMultiSpacedArguments(string commandLine)
{
    var isLastCharSpace = false;
    char[] parmChars = commandLine.ToCharArray();
    bool inQuote = false;
    for (int index = 0; index < parmChars.Length; index++)
    {
        if (parmChars[index] == '"')
            inQuote = !inQuote;
        if (!inQuote && parmChars[index] == ' ' && !isLastCharSpace)
            parmChars[index] = '\n';

        isLastCharSpace = parmChars[index] == '\n' || parmChars[index] == ' ';
    }

    return (new string(parmChars)).Split('\n');
}

0

एक NuGet पैकेज है जिसमें ठीक उसी प्रकार की कार्यक्षमता है जिसकी आपको आवश्यकता है:

Microsoft.CodeAnalysis.Common वर्ग में शामिल है CommandLineParser विधि के साथ SplitCommandLineIntoArguments

आप इसे इस तरह उपयोग करते हैं:

using Microsoft.CodeAnalysis;
// [...]
var cli = @"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam foo";
var cliArgs = CommandLineParser.SplitCommandLineIntoArguments(cli, true);

Console.WriteLine(string.Join('\n', cliArgs));
// prints out:
// /src:"C:\tmp\Some Folder\Sub Folder"
// /users:"abcdefg@hijkl.com"
// tasks:"SomeTask,Some Other Task"
// -someParam
// foo

-2

मुझे यकीन नहीं है कि अगर मैं आपको समझ गया, लेकिन क्या समस्या यह है कि चरित्र को फाड़नेवाला के रूप में इस्तेमाल किया जाता है, यह भी पाठ के अंदर पाया जाना है? (सिवाय इसके कि यह डबल के साथ बच गया है?)

यदि हां, तो मैं एक निर्माण करूंगा for लूप बनाऊंगा, और सभी उदाहरणों को प्रतिस्थापित जहां <"> </> (या एक और" सुरक्षित "चरित्र के साथ मौजूद है, लेकिन यह सुनिश्चित करें कि यह केवल <">>, और <""> को प्रतिस्थापित करता है

स्ट्रिंग को पुनरावृत्त करने के बाद, मैं पहले की तरह पोस्ट करूँगा, स्ट्रिंग को विभाजित करूँगा, लेकिन अब चरित्र पर <|> |


डबल "" डेज़्यूज़ इट्स ए @ ".." स्ट्रिंग शाब्दिक, द डबल "s इनसाइड द @" .. "स्ट्रिंग एक सामान्य स्ट्रिंग में \" बच गए "के बराबर है
एंटोन

"केवल प्रतिबंध (I beleive) यह है कि स्ट्रिंग्स स्पेस-सीमांकित हैं, जब तक कि स्पेस एक" ... "ब्लॉक" के भीतर नहीं जाता है -> हो सकता है कि एक बाज़ूका के साथ एक पक्षी की शूटिंग हो, लेकिन एक बूलियन डाल दिया जाता है जो "सच" होता है जब किसी उद्धरण के अंदर, और यदि "सत्य" के अंदर एक स्थान का पता लगाया जाता है, तो जारी रखें, अन्यथा <> = <|>
इसरार खान

-6

हां, स्ट्रिंग ऑब्जेक्ट में एक अंतर्निहित फ़ंक्शन Split()होता है, जो एक एकल पैरामीटर लेता है जो चरित्र को एक सीमांकक के रूप में देखने के लिए निर्दिष्ट करता है, और इसमें व्यक्तिगत मूल्यों के साथ स्ट्रिंग (स्ट्रिंग []] की एक सरणी देता है।


1
यह src: "C: \ tmp \ Some Folder \ Sub Folder" भाग को गलत तरीके से विभाजित करेगा।
एंटोन

स्ट्रिंग के अंदर के उद्धरणों के बारे में क्या है जो अस्थायी रूप से रिक्त स्थान पर विभाजन को बंद करते हैं?
डैनियल इयरविकर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.