我想编写一个程序,该程序将数字作为输入,输入多行,这些行由下面的标识/分隔;字符并打印出它们的总和。示例:
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, ";");
}
}
}
答案 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)
这里已经有可行的解决方案,但是我想提出另一个可能有助于理解某些概念的解决方案。
尽管您不能使用getc
和ungetc
,但我仍然会使用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;
}