C ::使用sscanf解析字符串,警告:上下文敏感:-(

时间:2010-08-24 10:24:25

标签: c scanf

我在C中有一些字符串(char *)并使用sscanf对其进行标记。

我正在生成C源代码,使用sscanf是最简单的解决方案,但是存在这个问题:

参数有正则表达式:

[$]([a-zA-Z0-9_-]{0,122})?[a-zA-Z0-9]

(从 $开始,可以包含numbersletters '-'和{{1} },但后来两个 位于参数名称的末尾。)

即。 :

'_'

问题是这个(简化)

$My_parameter1            //OK
$my-param-2               //OK
$_-this_-_is--my-_par     //OK
$My_parameter2-           //WRONG!
$My_parameter2_           //WRONG!

char _param1 [125]; //string that matches parameter name char _param2 [125]; //string that matches parameter name if ( sscanf(str, " $%124[a-zA-Z0-9_-] - $%124[a-zA-Z0-9_-] ", _param1, _param2) != 2 ) DO_FAIL; 上使用时,(显然)

" $parameter_one - $param-two "显然存在问题,因为sscanf会将第一项标记为"$param1-$param2"',然后无法找到'$param1-

有经验的'-'程序员可以看到如何简单地解决这个问题吗?

即:

C

...谢谢

5 个答案:

答案 0 :(得分:2)

简短回答:sscanf无法解决此问题,因为sscanf无法回溯。

至少只有一次sscanf电话就无法做到这一点。尝试像

这样的东西
if (sscanf(str, " $%124[a-zA-Z0-9_-]", _param1) != 1) DO_FAIL;
size_t _param1_len = strlen(_param1);
if (_param1[_param1_len-1] == '-') {
  _param[_param1_len-1] = '\0';
  _param1_len -= 1;
}
// parse rest '- $param2'
if (sscanf(str+_param1_len, ...

想法是一次解析一个令牌。您可以实现标识符解析 作为自己的函数,所以你可以重用它,因为你可能想要解析 看起来像"$foo + $bar"

答案 1 :(得分:1)

您似乎熟悉正则表达式。如果您在POSIX平台上,为什么不使用regcomp()/regexec()/regfree()?或PCRE哪个也可用作Windows的DLL?

我通常避免将sscanf()用于比阅读数字或字符串更复杂的事情。否则,我要么编写一个迷你FSM(用char消耗字符串char),要么使用正则表达式。

答案 2 :(得分:0)

sscanf()不支持正则表达式吗?

答案 3 :(得分:0)

我认为,使用sscanf没有简单的方法。 sscanf不是regexp的替代品。更短的应该是一个自制解决方案:

char *t,input[]="$my-param1-$my-param2";
if( (t=strstr(input,"-$")!=0 || (t=strstr(input,"_$")!=0 )
{
  *t=0;
  strcpy(param1,input);
  strcpy(param2,t+1);
}

好的,在令牌之间有空格也很容易:

char *t,*t1,input[]=" $my-param1 -  $my-param2 ";
if( (t=strchr(input,'$'))!=0 && (t1=strchr(t+2,'$'))!=0 )
{
  *--t1=0;
  while( t1>t+2 && strchr(" -_",*(t1-1)) )
    *--t1=0;
  while( !*t1 ) ++t1;
  while( *t1 && strchr(" -_",t1[strlen(t1)-1]) )
    t1[strlen(t1)-1]=0;
  strcpy(param1,t);
  strcpy(param2,t1);
}

答案 4 :(得分:0)

似乎使用sscanf不是最简单的解决方案,因为单独的sscanf无法处理这样的令牌。

但是,按字符解析这样的字符串非常简单。

您需要一个能够向前看并告知令牌结束位置的函数:

char *token_end(char *s)
{
    int specials = 0;
    for (; *s != '\0'; ++s) {
        if (*s == '_' || *s == '-')
            ++specials;
        else if (isalnum(*s))
            specials = 0;
        else
            break;
    }
    return s - specials;
}

在找到'$'之后传递指向第一个字符的指针,并返回指向令牌后第一个字符的指针。

现在,逐个字符地解析字符串。如果它是'$',请使用token_end查找令牌结束的位置,并从结尾继续;否则,该角色不属于令牌:

/* str is a pointer to a NULL-terminated string */
char *p = str;
while (*p != '\0') {
    if (*p == '$') {
        char *beg = p;
        char *end = token_end(p+1);
        if (end - beg > 1) {
            /* here, beg points to the '$' of the current token,
             * and end to the character just after the token */
            printf("token(%li,%li)", beg - str, end - str);
            /* parse the token, save it, etc... */
            p = end;
            continue;
        }
    }
    /* do something with a character which does not belong to a token... */
    printf("%c", *p);
    ++p;
}