다른 실행 파일에 전달할 명령 줄 매개 변수가 포함 된 단일 문자열이 있으며 명령 줄에 명령이 지정된 경우 C #과 동일한 방식으로 개별 매개 변수가 포함 된 string []을 추출해야합니다. 리플렉션을 통해 다른 어셈블리 진입 점을 실행할 때 string []이 사용됩니다.
이것에 대한 표준 기능이 있습니까? 아니면 매개 변수를 올바르게 분할하기 위해 선호하는 방법 (정규식?)이 있습니까? 공백을 올바르게 포함 할 수있는 ‘ “‘로 구분 된 문자열을 처리해야하므로 ”로 분할 할 수 없습니다.
예제 문자열 :
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"
};
명령 줄 구문 분석 라이브러리가 필요하지 않으며 생성되어야하는 String []을 가져 오는 방법 만 있으면됩니다.
업데이트 : C #에서 실제로 생성 된 것과 일치하도록 예상 결과를 변경해야했습니다 (분할 문자열에서 추가 “제거됨).
답변
받는 사람 또한 좋은 순수 관리 솔루션 에 의해 Earwicker , 그것은 윈도우도를 제공, 완벽을 위해서 언급 할만큼 가치가 될 수 있습니다CommandLineToArgvW
문자열의 배열로 문자열을 나누는 기능 :
LPWSTR *CommandLineToArgvW( LPCWSTR lpCmdLine, int *pNumArgs);
유니 코드 명령 줄 문자열을 구문 분석하고 표준 C 런타임 argv 및 argc 값과 유사한 방식으로 이러한 인수의 개수와 함께 명령 줄 인수에 대한 포인터 배열을 반환합니다.
C #에서이 API를 호출하고 관리 코드에서 결과 문자열 배열의 압축을 푸는 예는 ” 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);
}
}
답변
각 문자를 검사하는 기능을 기반으로 문자열을 분할하는 기능이 없다는 것이 나를 짜증나게합니다. 있다면 다음과 같이 작성할 수 있습니다.
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);
}
상황에 따라 빈 문자열이 생성 될 수 있지만 해당 정보가 다른 경우에 유용 할 수 있으므로이 함수에서 빈 항목을 제거하지 않습니다.
두 번째로 (더 평범하게) 문자열의 시작과 끝에서 일치하는 따옴표 쌍을 잘라내는 작은 도우미입니다. 표준 Trim 방법보다 까다 롭습니다. 각 끝에서 하나의 문자 만 잘라 내고 한쪽 끝에서만 잘라 내지 않습니다.
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");
구현에는 이해가되는 경우 인수 주변의 따옴표를 제거하는 추가 기능이 있습니다 (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');
}
답변
저는 Jeffrey L Whitledge의 답변을 받아 조금 향상 시켰습니다.
이제 작은 따옴표와 큰 따옴표를 모두 지원합니다. 다른 유형의 따옴표를 사용하여 매개 변수 자체에 따옴표를 사용할 수 있습니다.
또한 인수 정보에 기여하지 않으므로 인수에서 따옴표를 제거합니다.
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);
}
답변
Earwicker 의 훌륭하고 순수한 관리 솔루션 은 다음과 같은 인수를 처리하지 못했습니다.
Test("\"He whispered to her \\\"I love you\\\".\"", "He whispered to her \"I love you\".");
3 개의 요소를 반환했습니다.
"He whispered to her \"I
love
you\"."
그래서 다음은 “인용 된 \”escape \ “인용구”를 지원하는 수정입니다.
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\".");
또한 CommandLineToArgvW 를 사용 하는 Atif Aziz 의 수락 된 답변 도 실패했습니다. 4 개의 요소를 반환했습니다.
He whispered to her \
I
love
you".
이것이 미래에 그러한 솔루션을 찾는 데 도움이되기를 바랍니다.
답변
답변
나는 반복기를 좋아하고 요즘 LINQ 는 IEnumerable<String>
문자열 배열만큼 쉽게 사용할 수 있으므로 Jeffrey L 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();
}