使用 scanf() 和 fgets() 读取由空格、括号和逗号分隔的输入
Read an input that is separated by spaces, parenthesis, and commas with scanf() and fgets()
我有以下输入:
1 (2 ,3 ,4) lantern
括号之间的int输入数量未知,可能会延长一段时间。
我最初的想法是scanf()第一个int,然后创建一个while循环来确定何时扫描封闭的paranethsis。然后最后用fgets()得到最后的字符串,类似这样。
scanf("%d", &address); //first input
scanf("%c", &paren); //scan the '(' or ',' or ')'
int current_room = 0; //index for array inside parenthsis
while(paren == '(' || paren == ','){
scanf("%d,", adjoined_room[current_room]); //scan am int
scanf("%c", &paren); //scan a ',' or ')'
current_room++; //increase the index
}
当我打印地址、数组和字符串时,这会打印以下输出:
Address: 1
Item: (2 ,3 ,4) lantern
括号之间的输入整数从未设置为数组。是否有更好的方法来确定何时输入')'?
scanf
绝不能使用。曾经。但是......你可以尝试这样的事情:
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
void * xrealloc(void *buf, size_t num, size_t siz);
int
main(void)
{
size_t cap = 4;
char buf[1024];
int *x = xrealloc(NULL, cap, sizeof *x);
if( scanf("%d ( %d", x, x + 1) != 2 ){
errx(EXIT_FAILURE, "Ivalid input");
}
int *y = x + 2;
while( scanf(",%d", y) == 1 ){
if( ++y == x + cap ){
cap += 4;
x = xrealloc(x, cap, sizeof *x);
}
}
if( scanf(")%1023s", buf) != 1 ){
errx(EXIT_FAILURE, "Ivalid input");
}
for( unsigned i = 0; i < y - x; i += 1 ){
printf("x[%d] = %d\n", i, x[i]);
}
printf("%s\n", buf);
return 0;
}
void *
xrealloc(void *buf, size_t num, size_t siz)
{
char *b = buf;
b = realloc(b, num * siz);
if( b == NULL ){
perror("realloc");
exit(EXIT_FAILURE);
}
return b;
}
这不能正确处理带有尾随逗号的输入,例如:1 (2 ,3 ,4, ) lantern
,而且我敢肯定还有许多它不喜欢的其他输入。练习留给 reader.
你可能不想使用小到 4 的初始容量,但它方便简单测试。
问题是 scanf("%c",
将读取输入中的下一个字符,而不会跳过任何白色 space。如果要跳过whitespace,则格式中需要一个space,例如scanf(" %c",
。您还应该检查 scanf return 值以确保您得到一个整数
将其添加到您的代码中可以得到类似的东西:
if (scanf("%d", &address) != 1) { //first input
fprintf(stderr, "syntax error\n");
return; // not an integer -- do something else
}
scanf(" %c", &paren); //scan the '(' or ',' or ')'
int current_room = 0; //index for array inside parenthsis
while(paren == '(' || paren == ','){
if (scanf("%d", adjoined_room[current_room]) == 1) { //scan an int
current_room++; //increase the index
}
scanf(" %c", &paren); //scan a ',' or ')'
if (paren != ',' && paren != ')') {
fprintf(stderr, "syntax error\m");
return;
}
}
如果你想用交互式输入来做到这一点,你可能应该使用 fgets
或 getline
来读取整行,并使用 sscanf
来独立解析每一行,这样你就不会当一行中间有错误时,会让你的用户感到困惑。如果您想尝试多种不同的模式,“读取行 + sscanf”也非常有用(sscanf 在同一行上使用不同的格式找到第一个匹配的模式)。
这可能不是最受欢迎的答案,它可能有助于也可能不会帮助您实现近期目标,但我的理念是将输入读取为字节流并通过(粗略或复杂的)状态机进行解析:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define process_word(x) (printf("Got string \'%s\'\n", x))
#define process_number(x) (printf("Got number %lu\n", strtoul(x, NULL, 10)))
int main(void) {
int c;
int depth = 0;
size_t i;
char digitbuffer[256];
char alphabuffer[256];
while ((c = fgetc(stdin)) != EOF) {
switch (c) {
case ' ':
case ',':
break;
case '(':
depth++;
break;
case ')':
if (depth == 0) perror("Mismatched parenthesis, skipping");
else depth--;
break;
default:
if (isalpha(c)) {
memset(alphabuffer, 0, 256);
alphabuffer[0] = c;
i = 1;
while ((c = fgetc(stdin)) != EOF &&
isalpha(c) &&
i < 255) {
alphabuffer[i++] = c;
}
if (!isalpha(c) && c != EOF) ungetc(c, stdin);
process_word(alphabuffer);
}
else if (isdigit(c)) {
memset(digitbuffer, 0, 256);
digitbuffer[0] = c;
i = 1;
while ((c = fgetc(stdin)) != EOF &&
isdigit(c) &&
i < 255) {
digitbuffer[i++] = c;
}
if (!isdigit(c) && c != EOF) ungetc(c, stdin);
process_number(digitbuffer);
}
break;
}
}
return 0;
}
在我看来,这使您可以最大程度地控制处理特定数据格式。
当然,您可以定义自己的 process_word()
和 process_number()
函数。例如,如果 depth == 0
,process_number()
可能会将数字分配给记录的 address
字段,或者如果 depth == 1
,则将其添加到 adjacent_room[]
。 process_word()
可能会将字符串添加到同一记录的 item
字段。完全取决于你。 ¯\_(ツ)_/¯
我有以下输入:
1 (2 ,3 ,4) lantern
括号之间的int输入数量未知,可能会延长一段时间。
我最初的想法是scanf()第一个int,然后创建一个while循环来确定何时扫描封闭的paranethsis。然后最后用fgets()得到最后的字符串,类似这样。
scanf("%d", &address); //first input
scanf("%c", &paren); //scan the '(' or ',' or ')'
int current_room = 0; //index for array inside parenthsis
while(paren == '(' || paren == ','){
scanf("%d,", adjoined_room[current_room]); //scan am int
scanf("%c", &paren); //scan a ',' or ')'
current_room++; //increase the index
}
当我打印地址、数组和字符串时,这会打印以下输出:
Address: 1
Item: (2 ,3 ,4) lantern
括号之间的输入整数从未设置为数组。是否有更好的方法来确定何时输入')'?
scanf
绝不能使用。曾经。但是......你可以尝试这样的事情:
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
void * xrealloc(void *buf, size_t num, size_t siz);
int
main(void)
{
size_t cap = 4;
char buf[1024];
int *x = xrealloc(NULL, cap, sizeof *x);
if( scanf("%d ( %d", x, x + 1) != 2 ){
errx(EXIT_FAILURE, "Ivalid input");
}
int *y = x + 2;
while( scanf(",%d", y) == 1 ){
if( ++y == x + cap ){
cap += 4;
x = xrealloc(x, cap, sizeof *x);
}
}
if( scanf(")%1023s", buf) != 1 ){
errx(EXIT_FAILURE, "Ivalid input");
}
for( unsigned i = 0; i < y - x; i += 1 ){
printf("x[%d] = %d\n", i, x[i]);
}
printf("%s\n", buf);
return 0;
}
void *
xrealloc(void *buf, size_t num, size_t siz)
{
char *b = buf;
b = realloc(b, num * siz);
if( b == NULL ){
perror("realloc");
exit(EXIT_FAILURE);
}
return b;
}
这不能正确处理带有尾随逗号的输入,例如:1 (2 ,3 ,4, ) lantern
,而且我敢肯定还有许多它不喜欢的其他输入。练习留给 reader.
你可能不想使用小到 4 的初始容量,但它方便简单测试。
问题是 scanf("%c",
将读取输入中的下一个字符,而不会跳过任何白色 space。如果要跳过whitespace,则格式中需要一个space,例如scanf(" %c",
。您还应该检查 scanf return 值以确保您得到一个整数
将其添加到您的代码中可以得到类似的东西:
if (scanf("%d", &address) != 1) { //first input
fprintf(stderr, "syntax error\n");
return; // not an integer -- do something else
}
scanf(" %c", &paren); //scan the '(' or ',' or ')'
int current_room = 0; //index for array inside parenthsis
while(paren == '(' || paren == ','){
if (scanf("%d", adjoined_room[current_room]) == 1) { //scan an int
current_room++; //increase the index
}
scanf(" %c", &paren); //scan a ',' or ')'
if (paren != ',' && paren != ')') {
fprintf(stderr, "syntax error\m");
return;
}
}
如果你想用交互式输入来做到这一点,你可能应该使用 fgets
或 getline
来读取整行,并使用 sscanf
来独立解析每一行,这样你就不会当一行中间有错误时,会让你的用户感到困惑。如果您想尝试多种不同的模式,“读取行 + sscanf”也非常有用(sscanf 在同一行上使用不同的格式找到第一个匹配的模式)。
这可能不是最受欢迎的答案,它可能有助于也可能不会帮助您实现近期目标,但我的理念是将输入读取为字节流并通过(粗略或复杂的)状态机进行解析:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define process_word(x) (printf("Got string \'%s\'\n", x))
#define process_number(x) (printf("Got number %lu\n", strtoul(x, NULL, 10)))
int main(void) {
int c;
int depth = 0;
size_t i;
char digitbuffer[256];
char alphabuffer[256];
while ((c = fgetc(stdin)) != EOF) {
switch (c) {
case ' ':
case ',':
break;
case '(':
depth++;
break;
case ')':
if (depth == 0) perror("Mismatched parenthesis, skipping");
else depth--;
break;
default:
if (isalpha(c)) {
memset(alphabuffer, 0, 256);
alphabuffer[0] = c;
i = 1;
while ((c = fgetc(stdin)) != EOF &&
isalpha(c) &&
i < 255) {
alphabuffer[i++] = c;
}
if (!isalpha(c) && c != EOF) ungetc(c, stdin);
process_word(alphabuffer);
}
else if (isdigit(c)) {
memset(digitbuffer, 0, 256);
digitbuffer[0] = c;
i = 1;
while ((c = fgetc(stdin)) != EOF &&
isdigit(c) &&
i < 255) {
digitbuffer[i++] = c;
}
if (!isdigit(c) && c != EOF) ungetc(c, stdin);
process_number(digitbuffer);
}
break;
}
}
return 0;
}
在我看来,这使您可以最大程度地控制处理特定数据格式。
当然,您可以定义自己的 process_word()
和 process_number()
函数。例如,如果 depth == 0
,process_number()
可能会将数字分配给记录的 address
字段,或者如果 depth == 1
,则将其添加到 adjacent_room[]
。 process_word()
可能会将字符串添加到同一记录的 item
字段。完全取决于你。 ¯\_(ツ)_/¯