2

我有一个这样的字符串:

"00:00:00 000~00:02:00 0000|~00:01:00 0000;00:01:00 0000~",

我想得到像"00:00:00 000".

我的想法是首先将字符串拆分为";",然后拆分为"|",最后拆分为"~"

但问题是,如果它为空,我就无法获取它,例如"00:01:00 0000~",之后的部分 "~",我想获取它并为其设置一个默认值,然后将其存储在其他地方,但代码不起作用。问题是什么?

这是我的代码:

int main(int argc, char *argv[])
{

   char *str1, *str2, *str3, *str4, *token, *subtoken, *subt1, *subt2;
   char *saveptr1, *saveptr2, *saveptr3;
   int j;

   for (j = 1, str1 = argv[1]; ; j++, str1 = NULL) {
       token = strtok_r(str1, ";", &saveptr1);
       if (token == NULL)
           break;
       printf("%d: %s\n", j, token);

       int flag1 = 1; 
       for (str2 = token; ; str2 = NULL) {
           subtoken = strtok_r(str2, "|", &saveptr2);
           if (subtoken == NULL)
               break;
           printf("  %d: --> %s\n", flag1++, subtoken);
           int flag2 = 1;
           for(str3 = subtoken; ; str3 = NULL) {
                subt1 = strtok_r(str3, "~", &saveptr3);
                if(subt1 == NULL) {
                    break;
                }
                printf("      %d: --> %s\n",flag2++, subt1);
           }
       }
   }

   exit(EXIT_SUCCESS);
} /* main */
4

3 回答 3

2

如果首先使所有分隔符统一,则可以简化算法。首先替换所有出现的 , 和 | 加上~,那么解析会更容易。您可以通过 sed 或 vim 在外部执行此操作,也可以在 C 代码中以编程方式执行此操作。那么您应该能够轻松解决“NULL”问题。(就我个人而言,我不喜欢使用 strtok,因为它会修改原始字符串)。

于 2012-09-11T10:11:48.037 回答
2

在这种情况下,编写自定义解析器确实更容易。

下面的版本分配新的字符串,如果不需要分配新的内存,将add_string方法更改为只指向start,并设置start[len]为 0。

static int add_string( char **into, const char *start, int len )
{
    if( len<1 ) return 0;
    if( (*into = strndup( start, len )) )
        return 1;
    return 0;
}

static int is_delimeter( char x )
{
    static const char delimeters[] = { 0, '~', ',', '|',';' };
    int i;

    for( i=0; i<sizeof(delimeters); i++ )
        if( x == delimeters[i] )
            return 1;

    return 0;
}

static char **split( const char *data )
{
    char **res = malloc(sizeof(char *)*(strlen(data)/2+1));
    char **cur = res;
    int last_delimeter = 0, i;

    do {
        if( is_delimeter( data[i] ) )
        {
            if( add_string( cur, data+last_delimeter,i-last_delimeter) )
                cur++;
            last_delimeter = i+1;
        }
    } while( data[i++] );

    *cur = NULL;
    return res;
}

该方法的示例用法:

int main()
{
    const char test[] = "00:00:00 000~00:02:00 0000|~00:01:00 0000;00:01:00 0000~";
    char **split_test = split( test );
    int i = 0;

    while( split_test[i] )
    {
        fprintf( stderr, "%2d: %s\n", i, split_test[i] );
        free( split_test[i] );
        i++;
    }
    free( split_test );
    return 0;
}
于 2012-09-11T12:42:03.180 回答
1

与其拆分字符串,不如想出一个简单的有限状态机来解析字符串可能更合适。幸运的是,您的令牌似乎对其长度有上限,这使事情变得容易多了:

遍历字符串并区分四种不同的状态:

  • 当前字符不是分隔符,但前一个字符是(标记开始)
  • 当前字符是分隔符,前一个字符不是(标记结束)
  • 当前和前一个字符都不是分隔符(将它们存储在临时缓冲区中)
  • 当前和前一个字符都是分隔符(忽略它们,读取下一个字符)

应该可以想出一段非常短(10 行?)且简洁的代码来解析指定的字符串。

于 2012-09-11T10:59:52.983 回答