C #에서 명령 줄 매개 변수가 포함 된 문자열을 string []으로 분할
다른 실행 파일에 전달할 명령 줄 매개 변수가 포함 된 단일 문자열이 있으며 명령 줄에 명령이 지정된 경우 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".
이것이 미래에 그러한 솔루션을 찾는 데 도움이되기를 바랍니다.
Environment.GetCommandLineArgs ()
반복자 같은 I, 요즘 LINQ는 수 IEnumerable<String>
의 정신 아래의 필자 있도록, 캐릭터 라인의 배열로 쉽게 사용할 수 제프리 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();
}
이 The Code Project 기사 는 제가 과거에 사용한 것입니다. 좋은 코드이지만 작동 할 수도 있습니다.
이 MSDN 문서 는 C #에서 명령 줄 인수를 구문 분석하는 방법을 설명하는 유일한 항목입니다.
귀하의 질문에서 귀하는 정규식을 요청했으며 저는 열렬한 팬이며 사용자이므로 귀하와 동일한 인수 분할을 수행해야 할 때 간단한 해결책을 찾지 못하고 인터넷 검색을 한 후 제 자신의 정규식을 작성했습니다. 나는 짧은 솔루션을 좋아하므로 하나를 만들었고 여기에 있습니다.
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();
따옴표 안의 공백과 따옴표를 처리하고 묶인 ""을 "로 변환합니다. 코드를 자유롭게 사용하십시오!
순수하게 관리 솔루션은 도움이 될 수 있습니다. WINAPI 기능에 대한 "문제"주석이 너무 많으며 다른 플랫폼에서는 사용할 수 없습니다. 다음은 잘 정의 된 동작을 가진 내 코드입니다 (원하는 경우 변경할 수 있음).
이 string[] args
매개 변수를 제공 할 때 .NET / Windows가 수행하는 것과 동일해야 하며 여러 "흥미로운"값과 비교했습니다.
이것은 입력 문자열에서 각 단일 문자를 가져와 현재 상태에 대해 해석하여 출력과 새 상태를 생성하는 고전적인 상태 시스템 구현입니다. 상태 변수가 정의되어 escape
, inQuote
, hadQuote
및 prevCh
, 상기 출력에서 수집 currentArg
하고 args
.
실제 명령 프롬프트 (Windows 7)에서 실험을 통해 발견 한 몇 가지 전문 분야 는 따옴표로 묶인 범위 내에서 \\
생산 \
, \"
생산 "
, ""
생산 "
.
^
을 두배로하지 않을 때는 항상 사라 : 문자도, 마법 것 같다. 그렇지 않으면 실제 명령 줄에 영향을주지 않습니다. 이 동작에서 패턴을 찾지 못했기 때문에 내 구현이이를 지원하지 않습니다. 아마도 누군가 그것에 대해 더 많이 알고있을 것입니다.
이 패턴에 맞지 않는 것은 다음 명령입니다.
cmd /c "argdump.exe "a b c""
이 cmd
명령은 바깥 쪽 따옴표를 잡고 나머지는 그대로 사용하는 것 같습니다. 여기에 특별한 마법의 소스가 있어야합니다.
나는 내 방법에 대한 벤치 마크를하지 않았지만 상당히 빠르다고 생각한다. Regex
문자열 연결을 사용 하지 않고 수행하지 않지만 대신 a 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();
}
사용하다:
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' });
}
Vapor in the Alley 의 답변을 기반으로 ^ 이스케이프도 지원합니다.
예 :
- 이것은 시험이다
- 이
- 이다
- ㅏ
- 테스트
- 이것은 "이다"테스트
- 이
- 이다
- 테스트
- 이 ^ "is a ^"테스트
- 이
- "는
- ㅏ"
- 테스트
- 이 "" "는 ^^ 테스트입니다"
- 이
- ^ 테스트입니다
또한 여러 공백을 지원합니다 (공백 블록 당 인수를 한 번만 나눕니다).
오 젠장. 그게 다야 .. Eugh. 그러나 이것은 합법적 인 공식입니다. Microsoft에서 .NET Core 용 C #에서 Windows 전용 일 수도 있고 플랫폼 간일 수도 있지만 MIT 라이센스가 있습니다.
유용한 정보, 메서드 선언 및 주목할만한 주석을 선택하십시오.
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
이것은 MSVC C 라이브러리 또는 .NET Framework에서 .NET Framework에서 .NET Core로 포팅 된 코드 CommandLineToArgvW
입니다.
여기에 정규식으로 일부 헛소리를 처리하고 인수 0 비트를 무시하려는 반감이있는 시도가 있습니다. 약간 마법 같습니다.
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
.
현재 내가 가지고있는 코드는 다음과 같습니다.
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;
}
이스케이프 따옴표로는 작동하지 않지만 지금까지 언급 한 경우에는 작동합니다.
이것은 이스케이프 된 따옴표와 함께 작동하지 않는 Anton의 코드에 대한 응답입니다. 3 곳을 수정했습니다.
- 생성자 에 모두 StringBuilder 에 SplitCommandLineArguments , 어떤 대체 \ "를 함께 \ 연구
- SplitCommandLineArguments 의 for 루프 에서 이제 \ r 문자를 다시 \ " 로 바꿉니다 .
- 변경된 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;
}
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();
}
어제 게시 한 코드를 볼 수 있습니다.
파일 이름 + 인수를 string []으로 분할합니다. 짧은 경로, 환경 변수 및 누락 된 파일 확장자가 처리됩니다.
(처음에는 레지스트리의 UninstallString 용이었습니다.)
이 코드를 시도하십시오.
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;
}
포르투갈어로 작성되었습니다.
다음은 작업을 완료하는 한 줄입니다 (BurstCmdLineArgs (...) 메서드 내에서 모든 작업을 수행하는 한 줄 참조).
내가 가장 읽기 쉬운 코드 라인이라고 부르는 것은 아니지만 가독성을 위해 분리 할 수 있습니다. 의도적으로 간단하며 모든 인수 케이스 (예 : 분할 문자열 문자 구분 기호를 포함하는 파일 이름 인수)에 대해 잘 작동하지 않습니다.
이 솔루션은 그것을 사용하는 내 솔루션에서 잘 작동했습니다. 내가 말했듯이 가능한 모든 인수 형식 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;
}
}
}
내가 좋아하는 것을 찾을 수 없습니다. 작은 명령 줄에 대한 yield magic으로 스택을 엉망으로 만드는 것이 싫습니다 (테라 바이트의 스트림이라면 다른 이야기가 될 것입니다).
내 생각은 다음과 같이 큰 따옴표로 따옴표 이스케이프를 지원합니다.
param = "a 15" "화면이 나쁘지 않습니다"param2 = 'a 15 "화면이 나쁘지 않습니다'param3 =" "param4 = / param5
결과:
param = "a 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();
}
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();
}
}
이해가되는지 잘 모르겠지만 스플리터로 쓰인 캐릭터도 텍스트 안에서 발견되는 문제인가요? (단, 이중 "?"로 이스케이프 처리됩니다.)
그렇다면 for
루프를 만들고 < ">가있는 모든 인스턴스를 <|> (또는 다른"안전한 "문자로 대체하지만 <" ">가 아닌 <"> 만 대체해야합니다.
문자열을 반복 한 후 이전에 게시 한대로 문자열을 분할하지만 이제는 <|> 문자를 사용합니다.
예, 문자열 객체에는 Split()
검색 할 문자를 구분 기호로 지정하는 단일 매개 변수를 사용하고 개별 값이 포함 된 문자열 배열 (string [])을 반환하는 라는 내장 함수 가 있습니다.
'development' 카테고리의 다른 글
Chrome 확장 프로그램 개발을 위해 WebStorm을 어떻게 사용합니까? (0) | 2020.09.23 |
---|---|
Cygwin에 Pip-3.2 설치 (0) | 2020.09.23 |
iCloud 기본 사항 및 코드 샘플 (0) | 2020.09.22 |
Gitlab을 사용하여 코드 검토를 설정하는 방법은 무엇입니까? (0) | 2020.09.22 |
완벽한 연속 통합 환경을 위해 Vagrant와 Jenkins를 결합하는 방법은 무엇입니까? (0) | 2020.09.22 |