使用scanf()从括号中读取逗号分隔值

时间:2015-04-11 22:21:53

标签: c struct scanf

我有以下输入:

((1828,299),(2729,2553),(2797,2929),(2200,1383),(2894,876))

以及结构:

struct x{
int a;
int b;
}

如何使用scanf()读取输入以创建我的结构数组?

我试过了

scanf("%[^, ()],%d", &arr);
    if (i % 2 == 0){
        arr[i].x = scanf("%d");
    }else
        arr[i].y = scanf("%d");

但是当我试图打印这些值时,我得到了一些奇怪的字符

3 个答案:

答案 0 :(得分:3)

scanf()

的使用不正确
  1. scanf("%[^, ()],%d", ...)期望扫描并形成字符串("%[^, ()]")和int"%d")。代码仅提供用于保存int

  2. 的地方
  3. 代码未检查返回值表单scanf(),因此代码如果不知道任何扫描问题。

  4. 如果数据是,建议使用fgets()然后解析它。可以使用strtok() strtol() sscanf()。每个人的各种利弊。例如:

    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #define MAXN (5)
    // maximum size needed to print and `int`
    #define INTSIZE (sizeof (int )* CHAR_BIT  / 3 + 3)
    // Expect buffer size needed
    #define EXPECTEDSIZE (1+(2*INTSIZE + 4) + 3)
    
    void fooo() {
      int i;
    
      struct x {
        int a;
        int b;
      } xx[MAXN];
      char buf[EXPECTEDSIZE * 2]; // I favor 2x size buffers
      while (fgets(buf, sizeof buf, stdin)) {
        char *p = buf;
        if (*p++ != '(') {
          exit(EXIT_FAILURE);
        }
        for (i = 0; i < MAXN; i++) {
          int n; // use %n to locate scan completion
          int cnt = sscanf(p, " (%d ,%d ) %n", &xx[i].a, &xx[i].b, &n);
          fprintf(stderr, "cnt = %d '%s'\n", cnt, p);
          if (cnt != 2) {
            exit(EXIT_FAILURE);
          }
          p += n;
          if (p[0] != ',') {
            if (p[0] == ')') {
              i++;
              break; // Successfully reached the end
            }
            exit(EXIT_FAILURE);
          }
          p++;
        }
        int j;
        for (j=0; j< i; j++) {
          printf("%d  (%d ,%d )\n", j, xx[j].a, xx[j].b);
        }
      }
    }
    

答案 1 :(得分:1)

你得到奇怪字符的原因是scanf()没有返回扫描值,它返回与格式匹配的项目数。

您正在以调用未定义行为的方式调用scanf()。当你使用"%d"说明符时,它期望一个指向整数的指针作为参数,所以正确的方法是

if (scanf("%d", &arr[i].x) == 1)
    /* succesful */
else
    /* error */

*scanf()系列函数无法匹配您想要的模式,可以使用正则表达式库,也可以通过拆分(value, value)然后分别拆分它们的内容来解析字符串。

我想到的一种方法是将strtok()"),"一起使用但不计算最后一个元素,如果")"之间有空格,则很容易失败和","所以,状态机和一次解析一个字符的字符串可能是最好的方法。

这是我的意思的一个例子,我喜欢写这个

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

struct point
 {
    int x;
    int y;
 };

struct stack
 {
    char  *top;
    char **stack;
    size_t count;
    size_t size;
 };

void
stackinit(struct stack *stack)
 {
    if (stack == NULL)
        return;
    stack->top   = NULL;
    stack->stack = NULL;
    stack->count = 0;
    stack->size  = 0;
 }

void
stackfinish(struct stack *stack)
 {
    if (stack == NULL)
        return;
    free(stack->stack);

    stack->stack = NULL;
 }

char *
stacktop(struct stack *stack)
 {
    if ((stack == NULL) || (stack->count == 0))
        return NULL;
    return stack->stack[stack->count - 1];
 }

void
stackpush(struct stack *stack, char *value)
 {
    void *pointer;
    if (stack == NULL)
        return;
    if (stack->size == stack->count)
     {
        pointer = realloc(stack->stack, (stack->size + 100) * sizeof(char *));
        if (pointer == NULL)
            return;
        stack->stack = pointer;
        stack->size += 100;
     }

    if (stack->stack == NULL)
        return;

    stack->stack[stack->count] = value;
    stack->count              += 1;
 }

void
stackpop(struct stack *stack)
 {
    if ((stack == NULL) || (stack->count <= 0))
        return;
    stack->count              -= 1;
    stack->stack[stack->count] = NULL;
 }

void
extractpoint(char *string, struct point **points, size_t *count)
 {
    struct point point;
    void        *pointer;
    char        *tail;
    if ((string == NULL) || (points == NULL) || (count == NULL))
        return;
    tail = strchr(string, ')');
    if (tail == NULL)
        return;
    if (sscanf(string, "%d,%d", &point.x, &point.y) != 2)
        return;
    pointer = realloc(*points, (1 + count[0]) * sizeof(*points));
    if (pointer == NULL)
        return;
    points[0]             = pointer;
    points[0][count[0]++] = point;
 }

void
parse(char *input, struct point **points, size_t *count)
 {
    struct stack stack;

    stackinit(&stack);
    while (*(input++) != '\0')
     {
        char *top;
        switch (*input)
         {
        case '(':
            stackpush(&stack, input + 1);
            break;
        case ')':
            stackpop(&stack);
            break;
        case ',':
            top = stacktop(&stack);
            if (top == NULL)
                continue;
            extractpoint(top, points, count);
            break;
        default:
            break;
         }
     }
    stackfinish(&stack);

    return;
 }

int
main(void)
 {
    char          input[] = "((1828,299),((2729,2553),(2797,2929),(2200,1383),(2894,876))";
    size_t        count   = 0;
    struct point *points  = NULL;
    size_t        index   = 0;

    parse(input, &points, &count);
    for (index = 0 ; index < count ; ++index)
        fprintf(stdout, "%zu: %d, %d\n", index, points[index].x, points[index].y);
    free(points);

    return 0;
 }

答案 2 :(得分:1)

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

struct x{
    int a;
    int b;
};

int count(const char *s){
    if(s == NULL ||  *s != '(')
        return 0;//bad
    int n, a, b, count = 0;
    for(;;){
        n = -1;
        if(2!=sscanf(++s, "(%d,%d)%n", &a, &b, &n) || n < 0){
            return 0;
        } else {
            s += n;
            ++count;
            if(*s == ',')
                continue;
            else if(*s == ')')
                return count;
            else
                return 0;
        }
    }
}
void set(struct x *a, const char *s){
    int n, c = 0;
    while(2==sscanf(++s, "(%d,%d)%n", &a[c].a, &a[c].b, &n)){
        s += n;
        ++c;
    }
}

int main(void) {
    char *input = "((1828,299),(2729,2553),(2797,2929),(2200,1383),(2894,876))";
    int i, n = count(input);
    if(n == 0){
        printf("invalid format!\n");
        exit(EXIT_FAILURE);
    }
    struct x arr[n];
    set(arr, input);
    for(i = 0; i < n; ++i){
        printf("(%4d, %4d)\n", arr[i].a, arr[i].b);
    }

    return 0;;
}