你好我在C中有一个复杂的问题。我想拆分这个字符串(这个例子我包括所有的陷阱可能性)
"command1";"sleep 30; command2 -a ; command3";"command4="MyTest""
到:
command1
sleep 30; command2 -a ; command3
command4="MyTest"
字符串是" tab",元素由双引号和;
分隔
我如何使用strtok()
进行此操作?
我尝试将strtok()
与";"像这样的分隔符:
char mystring[1024]="\"command1\";\"sleep 30; command2 -a ; command3\";\"command4=\"MyTest\"\"";
token = strtok(mystring, "\";\"");
while( token != NULL ) {
error(token);
token = strtok(NULL, s);
}
但我有这个输出:
command1
"sleep 30
command2 -a
command3"
"command4="MyTest""
感谢您提前寻求帮助
答案 0 :(得分:0)
下面的示例根据请求解析输入字符串。它需要进行一些调整才能完全满足您的需求(将printf
替换为您的实际处理代码)。
要理解代码,请从描述字符串语法的顶部注释开始。然后转到main
- 使用调试器。
一些解释:
iss_t
是输入字符串流模拟。 iss_get_char
使用它来读取输入字符串中的字符。wschar_t
用于存储从输入流中读取的字符以及先前的空格。这是必要的,因为您没有指定"
和;
之间是否有空格。它由iss_get_wschar
和iss_peek_wschar
使用。iss_get_wschar
读取空格,直到找到非ws字符。iss_peek_wschar
与上述内容相同,但不会从流中提取字符。token_t
描述了由流构成的标记。parser_get_token
从流中提取令牌。它是perharps最重要的功能。parser_commands
从流中提取所有命令,它是解析器的主要功能。parser_command
从流中提取命令。您可以测试代码here。
#include <stdlib.h>
#include <ctype.h>
/*
commands:
EOF
|
command commands
command:
command_begin string command_end
string:
anything but command_end
command_begin:
QUOTE
command_end:
QUOTE SEMICOLON
|
QUOTE EOF
*/
struct iss_t
{
char* s;
char* g;
};
typedef struct iss_t iss_t;
char iss_get_char( iss_t* self )
{
if ( !*self->g )
return 0;
return *self->g++;
}
struct wschar_t
{
char c;
char s[ 1024 ];
};
typedef struct wschar_t wschar_t;
char iss_get_wschar( iss_t* self, wschar_t* r )
{
char* p = r->s;
while ( isspace( *p++ = r->c = iss_get_char( self ) ) )
;
*p = 0;
return r->c;
}
char iss_peek_wschar( iss_t* self )
{
char* savedg = self->g;
wschar_t t;
iss_get_wschar( self, &t );
self->g = savedg;
return t.c;
}
enum token_t
{
tk_done,
tk_beg,
tk_end,
tk_rest
};
typedef enum token_t token_t;
struct parser_t
{
iss_t* iss;
wschar_t wschar;
token_t token;
};
typedef struct parser_t parser_t;
token_t parser_get_token( parser_t* self )
{
iss_get_wschar( self->iss, &self->wschar );
// if done
if ( self->wschar.c == 0 )
return self->token = tk_done;
// not a quote
if ( self->wschar.c != '\"' )
return self->token = tk_rest;
// it is quote: check the next token
switch ( iss_peek_wschar( self->iss ) )
{
case 0:
case ';':
iss_get_wschar( self->iss, &self->wschar ); // eat
return self->token = tk_end;
default:
return self->token = tk_beg;
}
}
int parser_command( parser_t* self )
{
printf( "COMMAND: " );
if ( self->token != tk_beg )
{
printf( "command begin expected" );
return -1;
}
while ( parser_get_token( self ) == tk_rest || self->token == tk_beg )
printf( self->wschar.s );
if ( self->token != tk_end )
{
printf( "command end expected" );
return -1;
}
printf( "\n" );
return 0;
}
void parser_commands( parser_t* self )
{
while ( 1 )
{
switch ( parser_get_token( self ) )
{
case tk_done:
return;
default:
if ( parser_command( self ) < 0 )
return;
};
};
}
int main()
{
char* s = "\"command1\"; \"sleep 30; command2 -a ; command3\"; \"command4=\"MyTest\"\"";
iss_t iss = { s, s };
parser_t parser = { &iss };
parser_commands( &parser );
return 0;
}
答案 1 :(得分:-1)
您必须将字符串设为
char str[]="\"command1\";\"sleep 30; command2 -a ; command3\";\"command4=\"MyTest\"\"";
您无需使用strtok()
就可以使用sscanf()
char a[3][100];
sscanf(str, "\"%99[^\"]\";\"%99[^\"]\";\"%99s", a[0], a[1], a[2]);
a[2][strlen(a[2])-1]='\0';
printf("\n%s\n%s\n%s", a[0], a[1], a[2]);
最后a[2]
会以这种方式结束"
。我们使用a[2][strlen(a[2])-1]='\0';
删除它。
\"%[^\"]\"
表示读取"
,读取所有内容,直到另一个"
(此部分存储在字符数组中)并读取"
。
在sscanf()
中使用宽度说明符来防止缓冲区溢出。
您应该通过检查其返回值(例如
)来检查sscanf()
是否成功
if( sscanf(........) != 3)
{
printf("\nSomething went wrong.");
}
sscanf()
返回成功分配的数量,在这种情况下必须为3
。
输出是:
command1
sleep 30; command2 -a ; command3
command4="MyTest"
编辑:
数组大小100是任意的。如果需要更精确的内存分配,您可以先使用动态内存分配。像
sscanf(str, "\"%*[^\"]%n\";\"%*[^\"]%n\";\"%*s%n", &size[0], &size[1], &size[2]);
printf("\n%d, %d, %d", size[0], size[1], size[2]);
size[2] = size[2]-size[1]-4;
size[1] = size[1]-4;
size[0] = size[0]-1;
char *p[3];
for(int i=0; i<3; ++i)
{
if( (p[i]=malloc(sizeof(char)*size[i]))==NULL )
{
perror("Memory allocation failed!");
return 1;
}
}
%n
格式说明符返回读取的字符数。请参阅this帖子。
*
用于抑制。即,只是丢弃这些值。这个额外的scanf()
用于获取字符串的大小。
但这是针对此输入的。