C解析器程序添加以数字分隔的列表;多行使用read()输入

时间:2019-07-15 10:54:58

标签: c parsing strtok

我想编写一个程序,该程序将数字作为输入,输入多行,这些行由下面的标识/分隔;字符并打印出它们的总和。示例:

1 2 3; 4 5 6; 7 8 9;(enter)
10 11 12;(enter)
exit(enter)

我希望期望的输出完全像这样:

List 1: 6 (sum of 1 2 3)
List 2: 15 (sum of 4 5 6)
List 3: 24 (sum of 7 8 9)
List 4: 33 (sum of 10 11 12)

sum of a b c,则不必打印出来,但其结果以数字形式输入(输入),即按Enter /进入新行。

我要退出用户类型时退出。但是我的代码中出现分段错误错误。加上这段代码中的总和也得到了错误的值(我分别尝试过)。

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

int main() {
    char *b;
    int sum = 0;
    int rc;
    int i = 1;

    while (strcasecmp(b, "exit") != 0) {
        char buff[50];
        rc = read(0, buff, 50);
        if (rc == -1) {
            perror("");
            exit(0);
        }
        char *a = buff;
        b = strtok(a, "\n");
        char *c = strtok(b, ";");
        while (c != NULL) {
            char *d = strtok(c, " ");
            while (d != NULL) {
                int a = atoi(d);
                sum += a;
                d = strtok(NULL, " ");
                printf("List %d: %d", i, sum);
                i++;
            }
            c = strtok(NULL, ";");
        }
    }
}

3 个答案:

答案 0 :(得分:1)

您的代码中存在多个问题:

  • b是未初始化的指针,对其进行的读取和写入具有未定义的行为,很可能是分段错误的原因。

  • 您不应该使用POSIX低级函数来读取输入,它是不可移植的,并且输入可能无法按行块读取并且不会以null结尾......此外,-1返回值并不总是错误。

使用fgets()或其他标准流功能。

如果您可以假设列表不跨越多行并且总是以;结尾,那么这是一个简单的解决方案:

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

int sumlist(int n, char *str) {
    char *p, *q;
    int sum = 0, term;
    for (p = str;; p = q) {
        p += strspn(p, " \t\n");  // skip blanks
        if (*p == '\0')
            break;
        term = strtol(p, &q, 10);
        if (q == p) {
            printf("invalid input: %s\n", str);
            return -1;
        }
        sum += term;
    }
    printf("List %d: %d (sum of %s)\n", n, sum, str);
    return 0;
}  

int main() {
    char buf[200];
    int n = 1;
    char *p, *q;

    while (fgets(buf, sizeof buf, stdin) {
        for (p = str;;) {
            p += strspn(p, " \t\n");  // skip initial blanks
            if (*p == '\0')
                break;
            q = strchr(p, ';');
            if (q != NULL)
                *q = '\0';
            if (p == q) {
                p = q + 1;  // skip empty lists
                continue;
            }
            if (!strcmp(p, "exit"))
                break;
            sumlist(n++, p);
            if (q == NULL)
                break;
            p = q + 1;
        }
    }
    return 0;
}

如果您不能使用fgets()或任何标准流功能,请重写您自己的版本,使用read()一次从OS句柄读取一个字节,并仔细测试潜在的信号中断:< / p>

#include <errno.h>
#include <unistd.h>

char *my_gets(int hd, char *buf, size_t size) {
    size_t i;
    for (i = 0; i + 1 < size;) {
        ssize_t n = read(hd, &buf[i], 1);
        if (n != 1) {
            if (n == -1 && errno == EINTR)
               continue;
            break;
        }
        if (buf[i++] == '\n')
            break;
    }
    if (i == 0)
        return NULL;
    buf[i] = '\0';
    return buf;
}

int main() {
    char buf[200];
    int n = 1;
    char *p, *q;

    while (my_gets(0, buf, sizeof buf) {
        for (p = str;;) {
            p += strspn(p, " \t\n");  // skip initial blanks
            if (*p == '\0')
                break;
            q = strchr(p, ';');
            if (q != NULL)
                *q = '\0';
            if (p == q) {
                p = q + 1;  // skip empty lists
                continue;
            }
            if (!strcmp(p, "exit"))
                break;
            sumlist(n++, p);
            if (q == NULL)
                break;
            p = q + 1;
        }
    }
    return 0;
}

答案 1 :(得分:1)

您可以使用getchar并按如下方式解析整数,而无需使用strtok

int main() {

    int sum = 0; int rc; int i = 0, j = 0;
    char buff[50] = "";

   while(1)  {

      if (i>= sizeof buff) break; //not enough memory

      if (read(STDIN_FILENO, &buff[i], 1) < 1)  {break;} //read error

      if (strcasecmp(buff, "exit") == 0) break;
      else if (buff[i] == ';'){
            buff[i] = '\0';
            int a = atoi(buff);
            sum += a;
            printf("sum = %d\n", sum);
            sum = 0;
            i = 0;
            memset(buff, 0 , sizeof buff);
      }
      else if (buff[i] == ' '){
            buff[i] = '\0';
            int a = atoi(buff);
            sum += a;
            i = 0;
      }
      else if (buff[i] != '\n'){
        i++;
     }
    }
}

答案 2 :(得分:0)

这里已经有可行的解决方案,但是我想提出另一个可能有助于理解某些概念的解决方案。

尽管您不能使用getcungetc,但我仍然会使用get_buf概念来解决您的问题。我的解决方案一次读取一个字符,然后尝试将其转换成主循环可以switch可以打开的有效令牌。我认为,这是处理简单的“语言”(如您要解释的语言)解析的好方法。此外,它还具有相当的可扩展性,并且可以轻松添加其他令牌(例如+ - / *之类的数学运算)。

发生的事情是一个简短的描述:在get_char中,只要内部缓冲区为空,就会从STDIN中读取一个字节。如果不是,则返回缓冲区中的字符。 get_valid_token使用此功能,它返回定界符;或(可能是多位数)数字。这里需要能够“弄湿”角色。在main中,我们不断获取令牌并执行适当的操作,很好地将令牌的获取和解释分开。显然,这是一个快速而肮脏的程序,但它可能对您有用。

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

#define BUF_SIZ 2 /* get_buf never buffers more than one char by design */

static char get_buf[BUF_SIZ];
static char *get_buf_ptr = get_buf;

char get_char(int fd)
{
    char c;

    /* check buffer first */
    if (!(get_buf == get_buf_ptr))
        return *get_buf_ptr--;

    /* if buffer is empty, read from STDIN */
    if ((read(fd, &c, 1)) == -1) {
        perror("read");
        exit(1);
    }

    return c;
}

void unget_char(char c)
{
    *(++get_buf_ptr) = c;
}

void flush(int fd)
{
    char c;

    do {
        read(fd, &c, 1);
    } while (c != '\n');
}

char is_exit()
{
    if ((get_char(STDIN_FILENO)) != 'x') return 0;
    if ((get_char(STDIN_FILENO)) != 'i') return 0;
    if ((get_char(STDIN_FILENO)) != 't') return 0;
    flush(STDIN_FILENO); /* remove already buffered input */
    return 1;
}

char *get_valid_token(void)
{
    char c;
    char *out;
    char *out_ptr;

    out_ptr = out = (char *)malloc(sizeof(char)*BUFSIZ);
    while (1) {
        c = get_char(STDIN_FILENO);

        if (c == ';') {
            *out = ';';
            break;
        } else if (isdigit(c)) {
            *out = c;
            out_ptr++;

            /* get the rest of the digit */
            while (1) {
                c = get_char(STDIN_FILENO);
                if (isdigit(c)) {
                    *out_ptr++ = c;
                } else {
                    unget_char(c);
                    break;
                }
            }
            *out_ptr = '\0';
            break;
        } else if (c == 'e') {
            if (is_exit())
                exit(0);
        }
    };
    return out;
}

int main(void)
{
    char *t;
    int sum;

    sum = 0;
    while ((t = get_valid_token())) {
        switch (*t) {
            case ';':
                fprintf(stderr, "sum: %d\n", sum);
                sum = 0;
                break;
            default:
                sum += atoi(t);
                break;
        }
        free(t);
    }

    return 0;
}