5

对于可以在读取字符串中的字符,我需要非常严格。

我有一系列空格,后跟一个字符,然后是一系列空格。
示例:" c ", "c", ""," "

我需要找到一个允许我忽略该字符的格式说明符,但前提是它是这个特定字符而不是任何其他字符。这个序列" e "应该被中止。

我试过" %*[c] "了,但我的单元测试在某些情况下失败了——这让我相信这" %*[c] "是在寻找一个或多个'c'而不是零个或多个'c'

我写了一个小例子来帮助更好地说明我的问题。请记住,这只是一个最小示例。中心问题是我如何解析数量为零或单个字符之一。

#include <stdio.h>
#include <string.h>

unsigned match(const char * formula){
    unsigned e = 0, found = 0, s;
    char del;
    int parsed, pos, len = (int) strlen(formula); 
    const size_t soc = sizeof( char );
    del = ' ';
    parsed = sscanf_s( formula, " \" %*[(] X%*[^>]>> %u %*[)] %c %n", &s, &del, soc, &pos );// (X >> s )
    if( ( 2 == parsed ) && ( pos == len) && ( '"' == del ) ){
        printf("%6s:%s\n", "OK", formula);
    }else{
        printf("%6s:%s\n", "FAIL", formula);
        e += 1;
    }
    return e;
}

unsigned main( void )
{
    unsigned e = 0;

    printf("SHOULD BE OK\n");
    e += match("     \"X >> 3\""); //This one does not feature the optional characters
    e += match("     \"( X >> 3 ) \"");
    e += match("     \"( X >> 3 ) \"\r");

    printf("SHOULD FAIL\n");
    if ( 0 == match("     \"( Y >> 3 ) \"") ) e += 1;
    if ( 0 == match("     \"g X >> 3 ) \"") ) e += 1;
    if ( 0 == match("     \"( X >> 3.3-4.2 ) \"") ) e += 1;

    if( 0 != e ){ printf( "ERRORS: %2u\n", e ); }
    else{ printf( "all pass\n", e ); }
    return e;
}
4

1 回答 1

4

正如其他人向您指出的那样,sscanf不建议用于此目的。它无法捕获的情况是(可能出现或可能不会出现在"和之间的“可选” X。使用scanf, 如果有一个可选字段没有任何类型的分隔符来指示它丢失,那么确定它丢失的唯一方法是尝试解析它,注意它不存在,然后尝试再次解析它使用不同的扫描格式字符串。

parsed = sscanf( formula, " \" %*[(] X%*[^>]>> %u %*[)] %c %n", &s, &del, &pos );
if (parsed != 2) {
    parsed = sscanf( formula, " \" X%*[^>]>> %u %c %n", &s, &del, &pos );
}

该解决方案的其余部分描述了如何使用 POSIX<regex.h>基本正则表达式来解析它。

首先,您需要定义正则表达式并编译它。

const char *re =
    "[ \t]*\""                 /* match up to '"' */
    "[ \t]*(\\{0,1\\}[ \t]*"   /* match '(' if present */
    "X[ \t]*>>[ \t]*"          /* match 'X >>' */
    "\\([0-9][0-9]*\\)"        /* match number as subexpression */
    "[ \t]*)\\{0,1\\}[ \t]*"   /* match ')' if present */
    "\\(.\\)"                  /* match final delimiter as subexpression */
    "[ \t\r\n]*";              /* match trailing whitespace */
regex_t reg;
int r = regcomp(&reg, re, 0);
if (r != 0) {
    char buf[256];
    regerror(r, &reg, buf, sizeof(buf));
    fprintf(stderr, "regcomp: %s\n", buf);
    /*...*/
}

现在,您需要针对要匹配的字符串执行表达式。编译器将跟踪正则表达式中子表达式的数量,并将该数字放入reg.re_nsub. 但是,有一个不包含在该计数中的隐式子表达式。这是与提供的表达式匹配的完整字符串。这总是出现在第一场比赛中。因此,当您创建匹配数组时,请考虑到这一点。这就是为什么matches数组比reg.re_nsub.

unsigned match(const regex_t *preg, const char * formula){
    /*...*/
    int r;
    const int NSUB = preg->re_nsub + 1;
    regmatch_t matches[NSUB];

    r = regexec(preg, formula, NSUB, matches, 0);
    if (r == 0) {
        /* success */
        parsed = preg->re_nsub;
        s = atoi(formula + matches[1].rm_so);
        del = formula[matches[2].rm_so];
        pos = matches[0].rm_eo;
    } else {
        parsed = 0;
    }
    /*...*/

完成正则表达式后,您应该释放它(如果它已成功编译)。

regfree(&reg);
于 2013-05-24T17:24:12.787 回答