我该如何解析这样的字符串(6,8)

时间:2016-10-17 04:03:15

标签: c parsing segmentation-fault

我想解析一个格式为(6,8)的字符串,我想将6和8存储在不同的变量中。我试图使用" strtok"。但它给了我一个段错误

这是我的代码

 int main()
{
char a[80]="(6,8)";
parsing(a);

return 0;
}

int parsing(char* a)
{
char* word;
char temp[80] ;
stpcpy(temp,a);
char* new;
char **newendptr=NULL;
char **newnewendptr=NULL;

    int u,v;    
    word= strtok(temp, "(");
    word= strtok(NULL, ",");

    u=strtoumax(secword,newendptr,0);
    printf("%d\n",u);

    new= strtok(NULL, ")");
    printf("%s\n",new);

    v=strtoumax(new,newnewendptr,0);
    printf("%d %d",u,v);
    return 1;
}

2 个答案:

答案 0 :(得分:2)

仔细阅读strtok()的规范是值得的。

此代码有效:

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

static int parsing(char *a)
{
    char temp[80];
    strcpy(temp, a);

    printf("temp 0 = %p [[%s]]\n", (void *)temp, temp);
    char *word1 = strtok(temp, "(,)");
    printf("word 1 = %p [[%s]]\n", (void *)word1, word1 == 0 ? "<nothing>" : word1);
    char *word2 = strtok(NULL, "(,)");
    printf("word 2 = %p [[%s]]\n", (void *)word2, word2 == 0 ? "<nothing>" : word2);

    int u = strtoumax(word1, 0, 0);
    int v = strtoumax(word2, 0, 0);
    printf("%d %d\n", u, v);
    return 1;
}

int main(void)
{
    char a[80] = "(6,8)";
    parsing(a);
    return 0;
}

我机器上的输出是:

temp 0 = 0x7fff54844440 [[(6,8)]]
word 1 = 0x7fff54844441 [[6]]
word 2 = 0x7fff54844443 [[8]]
6 8

问题是,原始strtok()"("的调用会以(作为分隔符跳过开头strtok(),但之后却找不到另一个标记结尾的令牌,所以字符串的其余部分被消耗掉了。因此,对(的第二次调用无需处理并返回NULL。

固定代码避免了这个问题。初始分隔符必须包含,才能跳过该分隔符,并且必须包含)才能停止。第二个分隔符应包括strtoumax();其他字符并非严格需要。

由于您没有检查作为0的第二个参数的输出指针,因此每次都可能为NULL(也称为strtoumax())。使用int并将结果分配给strtoX()有点奇怪。使用给定的数据,可以,但更一般地说,它可能会丢失重要信息。 errno函数非常强大,但在报告超出界限值等方面也非常精细。这种用法会抛弃所有信息(在调用它之前你需要将uintmax_t设置为0 ,您必须将值保存在char c; if (sscanf(a, " (%d ,%d %c", &u, &v, &c) != 3 || c != ')') …oops — malformatted data… 变量中,以准确获取和保存信息。

在这种情况下,解析输入字符串的方式更为简洁(但不一定更简单):

char c;
int n;
if (sscanf(a, " (%d ,%d %c%n", &u, &v, &c, &n) != 3 || c != ')' || temp[n] != '\0')
    …Oops…

确保您知道为什么空间存在以及为什么它们在哪里。这可能需要仔细审查sscanf()的POSIX规范。你可以决定没有空格;你需要知道这样做的后果是什么。如果要确保解析整个字符串,请使用:

%n

请注意,@Lob(type = LobType.CLOB) 转换规范不计算在内,因此3不会更改。

答案 1 :(得分:1)

@Jonathan Leffler给出的答案似乎很好地涵盖了原始代码中没有用的内容。我只是想我会添加一个解析包含包含未知长度的n元组的字符串的解决方案。这里,parsing()函数将一个指向int的指针的地址作为参数,这是一个模拟的数字数组将被存储的位置,以及一个输入字符串,应格式为{{1} }。我对使用格式错误输入的此函数的行为不做任何承诺。该函数分配空间,将数字存储在模拟数组中,并返回找到的数字。

我包含了一个显示已解析数字的函数,甚至在函数中将调用包装到"(int1, int2, ...)"以捕获分配错误。我还添加了一些示例来说明它如何响应某些不同的输入。当然,由于我的realloc()版本分配内存,调用者负责释放内存。

以下是代码:

parsing()

这是输出:

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

size_t parsing(int **nums, char *a);
void * xrealloc(void *ptr, size_t size);
void shownums(int *nums, size_t n);

int main(void)
{
    int *nums = NULL;
    size_t n;
    char a[80] = "(6, 8)";
    char b[80] = "(2, 3, 5)";
    char c[80] = "(7)";
    char d[80] = "(11,)";
    char e[80] = "(2, 7, 1, 8, 2, 8, 1, 8, 2, 8, 5)";

    n = parsing(&nums, a);
    shownums(nums, n);

    n = parsing(&nums, b);
    shownums(nums, n);

    n = parsing(&nums, c);
    shownums(nums, n);

    n = parsing(&nums, d);
    shownums(nums, n);

    n = parsing(&nums, e);
    shownums(nums, n);

    /* Free allocated memory */
    free(nums);

    return 0;
}

size_t parsing(int **nums, char *a)
{
    size_t nums_sz = 0;
    char *res;

    while ((res = strtok(a, "(,)"))) {
        nums_sz++;
        *nums = realloc(*nums, sizeof(int) * nums_sz);
        (*nums)[nums_sz - 1] = atoi(res);
        a = NULL;
    }

    return nums_sz;
}

void * xrealloc(void *ptr, size_t size)
{
    void *ret = realloc(ptr, size);
    if (ret == NULL) {
        fprintf(stderr, "Unable to allocate memory\n");
        exit(EXIT_FAILURE);
    }

    return ret;
}

void shownums(int *nums, size_t n)
{
    for (size_t i = 0; i < n; i++)
        printf("%d ", nums[i]);
    putchar('\n');
}