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



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

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,打印出来不是必需的,但它们的结果为数字是(输入),即我按回车/进入新行。

当用户类型退出时,我将终止。但是我的代码中出现了分段错误错误。另外,在此代码中,总和也获得了错误的值(我单独尝试了)。

#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, ";");
}
}
}

您可以使用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] = '';
int a = atoi(buff);
sum += a;
printf("sum = %dn", sum);
sum = 0;
i = 0;
memset(buff, 0 , sizeof buff);
}
else if (buff[i] == ' '){
buff[i] = '';
int a = atoi(buff);
sum += a;
i = 0;
}
else if (buff[i] != 'n'){
i++;
}
}
}

代码中存在多个问题:

  • 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, " tn");  // skip blanks
if (*p == '')
break;
term = strtol(p, &q, 10);
if (q == p) {
printf("invalid input: %sn", 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, " tn");  // skip initial blanks
if (*p == '')
break;
q = strchr(p, ';');
if (q != NULL)
*q = '';
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()从操作系统句柄一次读取一个字节,并仔细测试潜在的信号中断:

#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] = '';
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, " tn");  // skip initial blanks
if (*p == '')
break;
q = strchr(p, ';');
if (q != NULL)
*q = '';
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;
}

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

虽然你不能使用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 = '';
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: %dn", sum);
sum = 0;
break;
default:
sum += atoi(t);
break;
}
free(t);
}
return 0;
}

相关内容

最新更新