【问题标题】:read comma separated values from brackets with scanf()使用 scanf() 从括号中读取逗号分隔值
【发布时间】:2015-04-11 22:21:53
【问题描述】:

我有以下输入:

((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");

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

【问题讨论】:

  • 你从哪里读到scanf() 使用正则表达式?
  • 我在stackoverflow上看到了一些例子。我是 C 的新手
  • @Ignacio Vazquez-Abrams:"%[ 按照 C 标准扫描字符类,而不是正则表达式。 OP 正在滥用这种格式,就像大多数新的 C 程序员一样。年长的程序员往往远离scanf 和朋友,忘记他们所有的怪癖......
  • @chqrlie:啊,现在我明白了。这是否表明我上次使用scanf() 从来没有?
  • @Ignacio Vazquez-Abrams:确实如此……我支持你。相反,我对scanf 有所了解的原因是因为我为一个小型(但标准)C 库实现了它。 scanf 几乎从来都不是正确的解析工具。就像strncpy 永远不应该进入标准一样。可惜这么多初学者都仰慕它的吸引力。

标签: c struct scanf


【解决方案1】:

scanf()的错误使用

  1. scanf("%[^, ()],%d", ...) 期望扫描并形成一个字符串 ("%[^, ()]") 和一个 int ("%d")。代码仅用于保存int

  2. 代码没有检查scanf()的返回值,所以如果没有发现任何扫描问题,就代码。

当数据为时,推荐使用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);
    }
  }
}

【讨论】:

  • @BLUEPIXY 同意。 Handle_BadFormat_or_EOF() 只不过是 OP 的占位符代码。由于 OP 没有指定如何处理故障或 EOF。当cnt == EOF 时,通常表示输入结束。其他值(如 0 或 1)意味着格式问题。在任何情况下,代码都可能需要跳出循环。我认为您的代码更优雅地处理错误,这就是我投票赞成它的原因。
  • @BLUEPIXY 是的,最后一个 ), cnt == 0 打破循环并使用 if (*p++ != ')') { 再次测试。
  • " (%d ,%d ) %n" :不要在元素之间消耗, 。(number,number),
  • @BLUEPIXY 你说得对,我的朋友。代码修改,
【解决方案2】:
#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;;
}

【讨论】:

  • 很好的解决方案。 @BLUEPIXY:你是我的英雄。如果我将 char *input 更改为 getch(),这是否也适用于用户的输入?
  • 从技术上讲,如果在一对整数之后缺少 ) 并调用未定义的行为,set 中的循环将无法正常运行。使用scanf 测试格式正确的输入非常容易出错。
  • @chqrlie set 是基于在 count 中进行解析的假设。
  • @BLUEPIXY:count() 函数有同样的问题:如果) 丢失,sscanf(++s, "(%d,%d)%n", &amp;a, &amp;b, &amp;n) 将不会设置n,但如果已解析 2 个整数,仍将返回 2 .以下将调用s += n; 中的未定义行为。 scanf和朋友几乎没用。
  • @BLUEPIXY:如果将n初始化为11,则不会检测到第三种无效格式。当您在所有 3 个测试用例中执行 s += n 时,n 未初始化。任何事情都可能发生,如果n恰好是11s将指向),如果n是一个很大的数字,程序可能会崩溃,UB。
【解决方案3】:

你得到奇怪字符的原因是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;
 }

【讨论】:

    猜你喜欢
    • 2013-02-12
    • 2016-09-28
    • 1970-01-01
    • 1970-01-01
    • 2020-04-04
    • 1970-01-01
    • 2020-01-15
    • 1970-01-01
    相关资源
    最近更新 更多