문자열을 argv/argc로 구문 분석
C에서 텍스트를 구문 분석하고 argv와 argc의 값을 얻는 방법이 있습니까? 마치 텍스트가 명령줄의 응용 프로그램에 전달된 것처럼?
이것은 Windows에서 작동할 필요가 없고 Linux에서만 작동합니다. 저는 또한 인수의 인용에 대해서도 신경 쓰지 않습니다.
표준 POSIX 기능을 사용하여 가장 간단한 답변을 제공한 사람이 없다는 것이 놀랍습니다.
http://www.opengroup.org/onlinepubs/9699919799/functions/wordexp.html
여기 제 기여가 있습니다.멋지고 짧지만 주의해야 할 점은 다음과 같습니다.
- strtok을 사용하면 원래 "commandLine" 문자열을 수정하여 \0 문자열 끝 부분으로 공백을 바꿉니다.
- argv[]는 "commandLine"을 가리키게 되므로 argv[]를 완료할 때까지 수정하지 마십시오.
코드:
enum { kMaxArgs = 64 };
int argc = 0;
char *argv[kMaxArgs];
char *p2 = strtok(commandLine, " ");
while (p2 && argc < kMaxArgs-1)
{
argv[argc++] = p2;
p2 = strtok(0, " ");
}
argv[argc] = 0;
이제 argc 및 argv를 사용하거나 "foo(int argc, char **argv)"처럼 선언된 다른 함수로 전달할 수 있습니다.
만약 glib 솔루션이 당신의 경우에 과잉 살상이라면, 당신은 스스로 코딩하는 것을 고려할 수 있습니다.
그러면 다음을 수행할 수 있습니다.
- 문자열을 스캔하고 인수가 몇 개 있는지 카운트합니다(그리고 당신은 argc를 얻습니다.
- char * 배열 할당(argv에 대한)
- 문자열을 다시 검색하고 할당된 배열에 포인터를 할당한 후 공백을 '\0'으로 바꿉니다(인수가 포함된 문자열을 수정할 수 없는 경우 해당 문자열을 복제해야 합니다).
- 당신이 할당한 것을 자유롭게 하는 것을 잊지 마세요!
아래 다이어그램은 (바라건대) 다음 사항을 명확히 할 것입니다.
aa bbb ccc "dd d" ee <- original string
aa0bbb0ccc00dd d00ee0 <- transformed string
| | | | |
argv[0] __/ / / / /
argv[1] ____/ / / /
argv[2] _______/ / /
argv[3] ___________/ /
argv[4] ________________/
가능한 API는 다음과 같습니다.
char **parseargs(char *arguments, int *argc);
void freeparsedargs(char **argv);
무료 구문 분석 변수()를 안전하게 구현하려면 추가적인 고려 사항이 필요합니다.
문자열이 매우 길고 두 번 검색하지 않으려면 argv 어레이에 더 많은 요소를 할당하고 필요한 경우 다시 할당하는 것과 같은 다른 방법을 고려할 수 있습니다.
편집: 제안된 솔루션입니다(따옴표로 묶은 인수를 처리하지 않습니다).
#include <stdio.h>
static int setargs(char *args, char **argv)
{
int count = 0;
while (isspace(*args)) ++args;
while (*args) {
if (argv) argv[count] = args;
while (*args && !isspace(*args)) ++args;
if (argv && *args) *args++ = '\0';
while (isspace(*args)) ++args;
count++;
}
return count;
}
char **parsedargs(char *args, int *argc)
{
char **argv = NULL;
int argn = 0;
if (args && *args
&& (args = strdup(args))
&& (argn = setargs(args,NULL))
&& (argv = malloc((argn+1) * sizeof(char *)))) {
*argv++ = args;
argn = setargs(args,argv);
}
if (args && !argv) free(args);
*argc = argn;
return argv;
}
void freeparsedargs(char **argv)
{
if (argv) {
free(argv[-1]);
free(argv-1);
}
}
int main(int argc, char *argv[])
{
int i;
char **av;
int ac;
char *as = NULL;
if (argc > 1) as = argv[1];
av = parsedargs(as,&ac);
printf("== %d\n",ac);
for (i = 0; i < ac; i++)
printf("[%s]\n",av[i]);
freeparsedargs(av);
exit(0);
}
언제나 멋진 입담이g_shell_parse_args()당신이 노리는 것처럼 들리는군요
만약 당신이 인용하는 것에 관심이 없다면, 이것은 과잉 살상일 수도 있습니다.공백을 토큰 문자로 사용하여 토큰화하기만 하면 됩니다.그렇게 하기 위해 간단한 일상을 쓰는 것은 오래 걸리지 않을 것입니다.
여러분이 메모리를 , 없이 한을 하는 입니다; 매 초 하고, 의 메를리너많사없않이한재번당의수패것됩것가경다최를면우정하의니악다매공니인초백다마입쉬울은하는행모로스는우할무용경이하지▁a▁if▁of▁string됩,,▁being가경를▁a▁thus▁assume면우정하최▁of니의악다▁without인공▁doing-▁memory▁you▁space▁it-▁just▁assuming▁in▁pass▁charactercase▁re백'▁second▁be 따라서 다음과 같은 문자열을 가정할 수 있습니다.n 최문자포가 됩니다.(n + 1) / 2 ( 논, 그고물 (으)ㄹ 수 있습니다.n인수 텍스트의 바이트 수(종단자 제외).
다음은 Windows 및 Unix(Linux, OSX 및 Windows에서 테스트됨)용 솔루션입니다.Valgrind와 Dr. Memory로 테스트했습니다.
POSIX 시스템의 경우 단어 xp를 사용하고 윈도우즈의 경우 명령줄ToArgvW를 사용합니다.
사이에서 변환됩니다.char **그리고.wchar_t **아름다운 Win32 API와 함께, 왜냐하면 그것은 없기 때문입니다.CommandLineToArgvA사용 가능(ANSI 버전).
#ifdef _WIN32
#include <windows.h>
#else
#include <wordexp.h>
#endif
char **split_commandline(const char *cmdline, int *argc)
{
int i;
char **argv = NULL;
assert(argc);
if (!cmdline)
{
return NULL;
}
// Posix.
#ifndef _WIN32
{
wordexp_t p;
// Note! This expands shell variables.
if (wordexp(cmdline, &p, 0))
{
return NULL;
}
*argc = p.we_wordc;
if (!(argv = calloc(*argc, sizeof(char *))))
{
goto fail;
}
for (i = 0; i < p.we_wordc; i++)
{
if (!(argv[i] = strdup(p.we_wordv[i])))
{
goto fail;
}
}
wordfree(&p);
return argv;
fail:
wordfree(&p);
}
#else // WIN32
{
wchar_t **wargs = NULL;
size_t needed = 0;
wchar_t *cmdlinew = NULL;
size_t len = strlen(cmdline) + 1;
if (!(cmdlinew = calloc(len, sizeof(wchar_t))))
goto fail;
if (!MultiByteToWideChar(CP_ACP, 0, cmdline, -1, cmdlinew, len))
goto fail;
if (!(wargs = CommandLineToArgvW(cmdlinew, argc)))
goto fail;
if (!(argv = calloc(*argc, sizeof(char *))))
goto fail;
// Convert from wchar_t * to ANSI char *
for (i = 0; i < *argc; i++)
{
// Get the size needed for the target buffer.
// CP_ACP = Ansi Codepage.
needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
NULL, 0, NULL, NULL);
if (!(argv[i] = malloc(needed)))
goto fail;
// Do the conversion.
needed = WideCharToMultiByte(CP_ACP, 0, wargs[i], -1,
argv[i], needed, NULL, NULL);
}
if (wargs) LocalFree(wargs);
if (cmdlinew) free(cmdlinew);
return argv;
fail:
if (wargs) LocalFree(wargs);
if (cmdlinew) free(cmdlinew);
}
#endif // WIN32
if (argv)
{
for (i = 0; i < *argc; i++)
{
if (argv[i])
{
free(argv[i]);
}
}
free(argv);
}
return NULL;
}
저는 직렬 포트 입력을 구문 분석하고 매개 변수로 제한된 명령 집합을 실행하는 작은 CLI를 가지고 있는 플레인 C의 임베디드 프로젝트를 위해 이 작업을 수행했습니다.
이것은 아마도 가장 깔끔하지는 않지만, 가능한 한 작고 효율적입니다.
int makeargs(char *args, int *argc, char ***aa) {
char *buf = strdup(args);
int c = 1;
char *delim;
char **argv = calloc(c, sizeof (char *));
argv[0] = buf;
while (delim = strchr(argv[c - 1], ' ')) {
argv = realloc(argv, (c + 1) * sizeof (char *));
argv[c] = delim + 1;
*delim = 0x00;
c++;
}
*argc = c;
*aa = argv;
return c;
}
테스트 대상:
int main(void) {
char **myargs;
int argc;
int numargs = makeargs("Hello world, this is a test", &argc, &myargs);
while (numargs) {
printf("%s\r\n", myargs[argc - numargs--]);
};
return (EXIT_SUCCESS);
}
맷 페이트렉의 LIBTINYC에는 argcargv.cpp라는 모듈이 있습니다. 이 모듈은 문자열을 가져와 따옴표로 묶은 인수 배열로 구문 분석합니다.Windows 전용이지만 매우 간단하므로 원하는 플랫폼으로 쉽게 이동할 수 있습니다.
저는 이것을 하기 위한 기능을 스스로 작성하게 되었습니다. 별로 좋지는 않지만 제 목적에 맞는 기능입니다. 미래에 이것이 필요한 다른 사람들을 위해 개선점을 제안하세요.
void parseCommandLine(char* cmdLineTxt, char*** argv, int* argc){
int count = 1;
char *cmdLineCopy = strdupa(cmdLineTxt);
char* match = strtok(cmdLineCopy, " ");
// First, count the number of arguments
while(match != NULL){
count++;
match = strtok(NULL, " ");
}
*argv = malloc(sizeof(char*) * (count+1));
(*argv)[count] = 0;
**argv = strdup("test"); // The program name would normally go in here
if (count > 1){
int i=1;
cmdLineCopy = strdupa(cmdLineTxt);
match = strtok(cmdLineCopy, " ");
do{
(*argv)[i++] = strdup(match);
match = strtok(NULL, " ");
} while(match != NULL);
}
*argc = count;
}
또 다른 구현을 고려해 보십시오.뛰어요.
#include <cctype> // <ctype.h> for isspace()
/**
* Parse out the next non-space word from a string.
* @note No nullptr protection
* @param str [IN] Pointer to pointer to the string. Nested pointer to string will be changed.
* @param word [OUT] Pointer to pointer of next word. To be filled.
* @return pointer to string - current cursor. Check it for '\0' to stop calling this function
*/
static char* splitArgv(char **str, char **word)
{
constexpr char QUOTE = '\'';
bool inquotes = false;
// optimization
if( **str == 0 )
return NULL;
// Skip leading spaces.
while (**str && isspace(**str))
(*str)++;
if( **str == '\0')
return NULL;
// Phrase in quotes is one arg
if( **str == QUOTE ){
(*str)++;
inquotes = true;
}
// Set phrase begining
*word = *str;
// Skip all chars if in quotes
if( inquotes ){
while( **str && **str!=QUOTE )
(*str)++;
//if( **str!= QUOTE )
}else{
// Skip non-space characters.
while( **str && !isspace(**str) )
(*str)++;
}
// Null terminate the phrase and set `str` pointer to next symbol
if(**str)
*(*str)++ = '\0';
return *str;
}
/// To support standart convetion last `argv[argc]` will be set to `NULL`
///\param[IN] str : Input string. Will be changed - splitted to substrings
///\param[IN] argc_MAX : Maximum a rgc, in other words size of input array \p argv
///\param[OUT] argc : Number of arguments to be filled
///\param[OUT] argv : Array of c-string pointers to be filled. All of these strings are substrings of \p str
///\return Pointer to the rest of string. Check if for '\0' and know if there is still something to parse. \
/// If result !='\0' then \p argc_MAX is too small to parse all.
char* parseStrToArgcArgvInsitu( char *str, const int argc_MAX, int *argc, char* argv[] )
{
*argc = 0;
while( *argc<argc_MAX-1 && splitArgv(&str, &argv[*argc]) ){
++(*argc);
if( *str == '\0' )
break;
}
argv[*argc] = nullptr;
return str;
};
사용코드
#include <iostream>
using namespace std;
void parseAndPrintOneString(char *input)
{
constexpr size_t argc_MAX = 5;
char* v[argc_MAX] = {0};
int c=0;
char* rest = parseStrToArgcArgvInsitu(input,argc_MAX,&c,v);
if( *rest!='\0' ) // or more clear `strlen(rest)==0` but not efficient
cout<<"There is still something to parse. argc_MAX is too small."<<endl;
cout << "argc : "<< c << endl;
for( int i=0; i<c; i++ )
cout<<"argv["<<i<<"] : "<<v[i] <<endl;
/*//or condition is `v[i]`
for( int i=0; v[i]; i++ )
cout<<"argv["<<i<<"] : "<<v[i] <<endl;*/
}
int main(int argc, char* argv[])
{
char inputs[][500] ={
"Just another TEST\r\n"
, " Hello my world 'in quotes' \t !"
, "./hi 'Less is more'"
, "Very long line with \"double quotes\" should be parsed several times if argv[] buffer is small"
, " \t\f \r\n"
};
for( int i=0; i<5; ++i ){
cout<<"Parsing line \""<<inputs[i]<<"\":"<<endl;
parseAndPrintOneString(inputs[i]);
cout<<endl;
}
}
출력:
Parsing line "Just another TEST\r\n":
argc : 3
argv[0] : Just
argv[1] : another
argv[2] : TEST
Parsing line " Hello my world 'in quotes' !":
There is still something to parse. argc_MAX is too small.
argc : 4
argv[0] : Hello
argv[1] : my
argv[2] : world
argv[3] : in quotes
Parsing line "./hi 'Less is more'":
argc : 2
argv[0] : ./hi
argv[1] : Less is more
Parsing line "Very long line with "double quotes" should be parsed several times if argv[] buffer is small":
There is still something to parse. argc_MAX is too small.
argc : 4
argv[0] : Very
argv[1] : long
argv[2] : line
argv[3] : with
Parsing line "
":
argc : 0
동적 메모리 할당(예: 내장형)을 사용하지 않으려는 사용자를 위한 솔루션
는 가쓴내라고 썼습니다.tokenise_to_argc_argv()니다내장경다사우용음합을의트프를 사용하는 strtok_r()명령 문자열을 argc 및 argv 형식으로 토큰화하는 기준으로 사용합니다.여기 있는 대부분의 답변과 달리, 저는 보통 정적으로 메모리를 할당합니다.이 따서나구당다신음상의한가있가다정니의 있다고 합니다.argv_length대부분의 일반적인 임베디드 애플리케이션의 경우 이 정도면 충분합니다.빠르게 사용할 수 있도록 샘플 코드도 아래에 포함하였습니다.
int tokenise_to_argc_argv(
char *buffer, ///< In/Out : Modifiable String Buffer To Tokenise
int *argc, ///< Out : Argument Count
char *argv[], ///< Out : Argument String Vector Array
const int argv_length ///< In : Maximum Count For `*argv[]`
)
{ /* Tokenise string buffer into argc and argv format (req: string.h) */
int i = 0;
for (i = 0 ; i < argv_length ; i++)
{ /* Fill argv via strtok_r() */
if ( NULL == (argv[i] = strtok_r( NULL , " ", &buffer)) ) break;
}
*argc = i;
return i; // Argument Count
}
참고:
- 가능해야 가 삽입됨에 따라).
\0버퍼로 이동하여 문자열 토큰을 구분합니다. - 이함 strtok_r 은사중다니를 사용하고 .
" "공백 문자를 유일한 구분 기호로 사용합니다.을 모방합니다.main(int argc, char *argv[])일반적인 명령줄 인터페이스에서 사용할 수 있습니다. - 이 함수는 malloc나 calloc를 사용하지 않고 argv 배열을 별도로 할당하고 argv의 길이를 명시적으로 제공해야 합니다.이는 내장된 장치에서 이 기능을 사용하기 때문에 수동으로 할당하는 것이 좋습니다.
strtok_r()스레드 안전하기 때문에 사용됩니다(이후).strtok()내부 정적 포인터 사용).또한 표준 C 라이브러리의 일부입니다.string.h그래서 매우 휴대성이 좋습니다.
아래는 데모 코드와 출력입니다.또한 이는 tokenise_to_argc_argv()가 대부분의 문자열 케이스를 처리할 수 있으므로 테스트되었음을 보여줍니다.또한 이 기능은 malloc나 calloc에 의존하지 않으므로 임베디드 사용에 적합합니다(사용 후).stdint.h유형)을 선택합니다.
데모 코드
/*******************************************************************************
Tokenise String Buffer To Argc and Argv Style Format
Brian Khuu 2017
*******************************************************************************/
#include <stdio.h> // printf()
#include <ctype.h> // isprint()
#include <string.h> // strtok_r()
/**-----------------------------------------------------------------------------
@brief Tokenise a string buffer into argc and argv format
Tokenise string buffer to argc and argv form via strtok_r()
Warning: Using strtok_r will modify the string buffer
Returns: Number of tokens extracted
------------------------------------------------------------------------------*/
int tokenise_to_argc_argv(
char *buffer, ///< In/Out : Modifiable String Buffer To Tokenise
int *argc, ///< Out : Argument Count
char *argv[], ///< Out : Argument String Vector Array
const int argv_length ///< In : Maximum Count For `*argv[]`
)
{ /* Tokenise string buffer into argc and argv format (req: string.h) */
int i = 0;
for (i = 0 ; i < argv_length ; i++)
{ /* Fill argv via strtok_r() */
if ( NULL == (argv[i] = strtok_r( NULL, " ", &buffer)) ) break;
}
*argc = i;
return i; // Argument Count
}
/*******************************************************************************
Demonstration of tokenise_to_argc_argv()
*******************************************************************************/
static void print_buffer(char *buffer, int size);
static void print_argc_argv(int argc, char *argv[]);
static void demonstrate_tokenise_to_argc_argv(char buffer[], int buffer_size);
int main(void)
{ /* This shows various string examples */
printf("# `tokenise_to_argc_argv()` Examples\n");
{ printf("## Case0: Normal\n");
char buffer[] = "tokenising example";
demonstrate_tokenise_to_argc_argv(buffer, sizeof(buffer));
}
{ printf("## Case1: Empty String\n");
char buffer[] = "";
demonstrate_tokenise_to_argc_argv(buffer, sizeof(buffer));
}
{ printf("## Case2: Extra Space\n");
char buffer[] = "extra space here";
demonstrate_tokenise_to_argc_argv(buffer, sizeof(buffer));
}
{ printf("## Case3: One Word String\n");
char buffer[] = "one-word";
demonstrate_tokenise_to_argc_argv(buffer, sizeof(buffer));
}
}
static void demonstrate_tokenise_to_argc_argv(char buffer[], int buffer_size)
{ /* This demonstrates usage of tokenise_to_argc_argv */
int argc = 0;
char *argv[10] = {0};
printf("* **Initial State**\n");
print_buffer(buffer, buffer_size);
/* Tokenise Command Buffer */
tokenise_to_argc_argv(buffer, &argc, argv, sizeof(argv));
printf("* **After Tokenizing**\n");
print_buffer(buffer, buffer_size);
print_argc_argv(argc,argv);
printf("\n\n");
}
static void print_buffer(char *buffer, int size)
{
printf(" - Buffer Content `");
for (int i = 0 ; i < size; i++) printf("%c",isprint(buffer[i])?buffer[i]:'0');
printf("` | HEX: ");
for (int i = 0 ; i < size; i++) printf("%02X ", buffer[i]);
printf("\n");
}
static void print_argc_argv(int argc, char *argv[])
{ /* This displays the content of argc and argv */
printf("* **Argv content** (argc = %d): %s\n", argc, argc ? "":"Argv Is Empty");
for (int i = 0 ; i < argc ; i++) printf(" - `argv[%d]` = `%s`\n", i, argv[i]);
}
산출량
tokenise_to_argc_argv()예
사례 0: 일반
- 초기 상태
- 버퍼 내용
tokenising example0HEX : 746F 6B 656E 69 7366E 67 20 65 78 61 6D 70 6C 6500
- 버퍼 내용
- 토큰화 후
- 버퍼 내용
tokenising0example0HEX : 746F 6B 656E 69 7366E 6700 65 78 61 6D 70 6C 6500
- 버퍼 내용
- Argv 함량(argc = 2):
argv[0]=tokenisingargv[1]=example
사례 1: 빈 문자열
- 초기 상태
- 버퍼 내용
0HEX: 00
- 버퍼 내용
- 토큰화 후
- 버퍼 내용
0HEX: 00
- 버퍼 내용
- Argv 함량(argc = 0): Argv가 비어 있습니다.
사례 2: 추가 공간
- 초기 상태
- 버퍼 내용
extra space here0HEX : 65 78 74 72 61 20 73 70 61 63 65 20 68 65 72 6500
- 버퍼 내용
- 토큰화 후
- 버퍼 내용
extra0 space0here0HEX : 65 78 74 72 6100 20 73 70 61 63 65 68 72 6500
- 버퍼 내용
- Argv 함량(argc = 3):
argv[0]=extraargv[1]=spaceargv[2]=here
사례 3: 한 단어 문자열
- 초기 상태
- 내용물 버용내
one-word0: 65 HEX : 6F 6E 65 2D 776F 72400
- 내용물 버용내
- 토큰화 후
- 내용물 버용내
one-word0: 65 HEX : 6F 6E 65 2D 776F 72400
- 내용물 버용내
- Argv 함량(argc = 1):
argv[0]=one-word
도구 체인에 string.h 또는 strtok_r()이 누락되었습니까?
어떤 이유로 인해 툴체인에 strtok_r()이 없는 경우.이 단순화된 버전의 strtok_r()을 사용할 수 있습니다.strtok_r()의 GNU C 구현체의 수정된 버전이지만 공백 문자만 지원하도록 단순화되었습니다.
이것을 사용하려면 위에 놓기만 하면 됩니다.tokenise_to_argc_argv()그런 다음 교체합니다.strtok_r( NULL, " ", &buffer)와 함께strtok_space(&buffer)
/**-----------------------------------------------------------------------------
@brief Simplied space deliminated only version of strtok_r()
- save_ptr : In/Out pointer to a string. This pointer is incremented by this
function to find and mark the token boundry via a `\0` marker.
It is also used by this function to find mutiple other tokens
via repeated calls.
Returns:
- NULL : No token found
- pointer to start of a discovered token
------------------------------------------------------------------------------*/
char * strtok_space(char **save_ptr)
{ /* strtok_space is slightly modified from GNU C Library `strtok_r()` implementation.
Thus this function is also licenced as GNU Lesser General Public License*/
char *start = *save_ptr;
char *end = 0;
if (*start == '\0') {
*save_ptr = start;
return NULL;
}
/* Scan leading delimiters. */
while(*start == ' ') start++;
if (*start == '\0') {
*save_ptr = start;
return NULL;
}
/* Find the end of the token. */
end = start;
while((*end != '\0') && (*end != ' ')) end++;
if (*end == '\0') {
*save_ptr = end;
return start;
}
/* Terminate the token and make *SAVE_PTR point past it. */
*end = '\0';
*save_ptr = end + 1;
return start;
}
제가 쓴 이 글도 인용문을 고려합니다(네스트되지는 않음).
얼마든지 기부하세요.
/*
Tokenize string considering also quotes.
By Zibri <zibri AT zibri DOT org>
https://github.com/Zibri/tokenize
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
int main(int argc, char *argv[])
{
char *str1, *token;
int j;
char *qstart = NULL;
bool quoted = false;
if (argc != 2) {
fprintf(stderr, "Usage: %s string\n", argv[0]);
exit(EXIT_FAILURE);
}
for (j = 1, str1 = argv[1];; j++, str1 = NULL) {
token = strtok(str1, " ");
if (token == NULL)
break;
if ((token[0] == 0x27) || (token[0] == 0x22)) {
qstart = token + 1;
quoted = true;
}
if ((token[strlen(token) - 1] == 0x27) || (token[strlen(token) - 1] == 0x22)) {
quoted = false;
token[strlen(token) - 1] = 0;
printf("%d: %s\n", j, qstart);
} else {
if (quoted) {
token[strlen(token)] = 0x20;
j--;
} else
printf("%d: %s\n", j, token);
}
}
if (quoted) {
fprintf(stderr, "String quoting error\n");
return EXIT_FAILURE;
} else
return EXIT_SUCCESS;
}
출력 예:
$ ./tokenize "1 2 3 '4 5 6' 7 8 \"test abc\" 10 11"
1: 1
2: 2
3: 3
4: 4 5 6
5: 7
6: 8
7: test abc
8: 10
9: 11
을 내프젝줄끊합니다어야을는트로 분할해야 합니다.argc그리고.argv.
토레크의 꽤 훌륭한 코드를 발견했습니다.하지만 입력 버퍼가 바뀌기 때문에 필요에 맞게 수정했습니다.
명령행에 입력할 때 따옴표 혼합을 처리하기 위해 조금 더 넣었기 때문에 동작이 (완전하지는 않지만) Linux Shell과 비슷합니다.
참고: 이 기능은 원래 문자열을 편집하지 않으므로 입력 버퍼(오류 보고서 등)를 재사용할 수 있습니다.
void remove_quote(char* input){
//Implementing yourself to remove quotes so it would be completely like Linux shell
}
size_t cmd_param_split(char *buffer, char *argv[], size_t argv_max_size)
{
char *p, *start_of_word;
int c, i;
enum states { DULL=0, IN_WORD, IN_STRING, QUOTE_DOUBLE,QUOTE_SINGLE } state = DULL;
size_t argc = 0;
int quote = 0;
for (p = buffer; argc < argv_max_size && *p != '\0'; p++) {
c = (unsigned char) *p;
printf("processing %c, state = %d\n", c,state);
switch (state) {
case DULL:
if (isspace(c)) {
continue;
}
if (c == '"' ||c == '\'') {
quote = c;
state = IN_STRING;
start_of_word = p + 1;
continue;
}
state = IN_WORD;
start_of_word = p;
continue;
case IN_STRING:
if (c == '"' || c == '\'') {
if (c!=quote)
continue;
else
quote = 0;
strncpy(argv[argc],start_of_word, p - start_of_word);
remove_quote(argv[argc]);
argc++;
state = DULL;
}
continue;
case IN_WORD:
if(quote==0 && (c == '\"' ||c == '\''))
quote = c;
else if (quote == c)
quote = 0;
if (isspace(c) && quote==0) {
strncpy(argv[argc],start_of_word, p - start_of_word);
remove_quote(argv[argc]);
argc++;
state = DULL;
}
continue;
}
}
if (state != DULL && argc < argv_max_size){
strncpy(argv[argc],start_of_word, p - start_of_word);
remove_quote(argv[argc]);
argc++;
}
if (quote){
printf("WARNING: Quote is unbalanced. This could lead to unwanted-behavior\n");
for(i = 0;i<argc;i++)
printf("arg %d = [%s]\n",i,argv[i]);
printf("Original buffer: [%s]\n",buffer);
}
return argc;
}
int main()
{
int i=0;
int argc;
char* argv[64];
for(i=0;i<64;i++){
argv[i] = malloc(256);
memset(argv[i],0x0,256);
}
char* buffer="1 2 3 \'3 4\"567\' \"bol\'obala\" 2x2=\"foo\"";
argc = cmd_param_split(buffer,argv,64);
for(i = 0;i<argc;i++)
printf("arg %d = [%s]\n",i,argv[i]);
return 0;
}
아래 문자열로 테스트됨
1. "1 2 3 \'3 4\"567\' \"bol\'obala\" 2x2=\"foo\""
arg 0 = [1]
arg 1 = [2]
arg 2 = [3]
arg 3 = [3 4"567]
arg 4 = [bol'obala]
arg 5 = [2x2="foo"]
2. "./foo bar=\"Hanoi HoChiMinh\" exp='foo123 \"boo111' mixquote \"hanoi \'s\""
arg 0 = [./foo]
arg 1 = [bar="Hanoi HoChiMinh"]
arg 2 = [exp='foo123 "boo111']
arg 3 = [mixquote]
arg 4 = [hanoi 's]
그러나 Linux 셸은 라즈베리파이에서 테스트한 cmd 라인에서 실행할 때 아래와 같이 혼합된 경우에도 따옴표를 제거합니다.
./foo bar="Hanoi HoChiMinh" exp='foo123 "boo111' mixquote "hanoi 's"
arg 0 = [./foo]
arg 1 = [bar=Hanoi HoChiMinh]
arg 2 = [exp=foo123 "boo111]
arg 3 = [mixquote]
arg 4 = [hanoi 's]
따라서 전체 Linux 셸의 동작을 모방하고 싶다면 위에 공백으로 남겨둔 quote remove_quote() 함수를 제거하는 데 조금만 더 노력하면 됩니다.
안타깝게도 C++이지만 이런 종류의 라이브러리를 검색할 수 있는 다른 사람들에게는 다음을 추천합니다.
ParamContainer - 사용이 간편한 명령줄 매개 변수 구문 분석기
정말 작고 정말 쉽습니다.
p.addParam("long-name", 'n', ParamContainer::regular,
"parameter description", "default_value");
programname --long-name=value
cout << p["long-name"];
>> value
내 경험에 비추어 볼 때:
- 매우 유용하고 간단한
- 생산이 안정된.
- (나에 의해) 잘 시험된
언급URL : https://stackoverflow.com/questions/1706551/parse-string-into-argv-argc
'programing' 카테고리의 다른 글
| 유레카 서비스, 유레카 클라이언트, 유레카 인스턴스 및 유레카 서버란? (0) | 2023.08.20 |
|---|---|
| 정의자('''@''')로 지정된 사용자가 존재하지 않습니다. (0) | 2023.08.20 |
| 애플리케이션을 다시 시작한 후 스프링 부트 jpa findbyid()가 작동을 중지함 (0) | 2023.08.20 |
| window.open()은(는) AJAX 성공 시 다르게 작동합니다. (0) | 2023.08.20 |
| jQuery post()(일련화 및 추가 데이터 포함) (0) | 2023.08.20 |