【问题标题】:The first char of my output is omitted in C我的输出的第一个字符在 C 中被省略了
【发布时间】:2017-07-19 03:33:27
【问题描述】:

我似乎无法弄清楚我的输出发生了什么。我正在读取多行用户输入并输出超出下限的相应输入。由于某种原因,当我输出时,输出的字符串省略了字符串的第一个字符。谁能告诉我为什么会这样?

#include <stdio.h>

typedef struct{
   char  name[4];
   int   population;
} state;

enum { MAX_STATES = 10 };

int main()
{
    state myStates[MAX_STATES];

    int c;
    int i = 0;

    while ((c = getchar())!= EOF)
    {
        scanf("%s %d\n", myStates[i].name, &myStates[i].population);
        i++; 
    }

    // printf("Last character is [%d]\n", c);
    printf("");

    if (c <= 0)
    {
        for(int j = 0; j <= MAX_STATES; j++)
        {
            if(myStates[j].population >= 10)
                printf("%s %d\n", myStates[j].name, myStates[j].population);
            else
                break;
        }
    }

    return 0;
}

输入:

TX 23
CA 45

输出:

X 23
A 45

更新代码:

#include <stdio.h>

typedef struct{
   char  name[4];
   int   population;
} State;

enum { MAX_STATES = 10 };

int main()
{
    State myStates[MAX_STATES];

    int i, j;

    // Function to read in multiple lines (up to 10) of user input; loop 
    // controls in place, detects format problems, prevents string buffer 
    // overflows.
    for (i = 0; i < MAX_STATES; i++)
    {
        if (scanf("%2s %d\n", myStates[i].name, &myStates[i].population) != 2)
            break;
    }

    // Function to output (stdout) array of State structs that exceed 10 
    // population. 
    for(j = 0; j < i; j++)
        {
            if(myStates[j].population >= 10)
                printf("%s %d\n", myStates[j].name, myStates[j].population);
            else
                break;
        }

    return 0;
}

发布的输出只会持续到输入小于 10 并跳出循环。当我没有那个 break 语句时,我在最后一行得到了垃圾输出。有什么改进输出的建议吗?

【问题讨论】:

  • 可能是因为在您将其分配给州名之前,它已在您的 getchar 调用中被读取。不知道你的输入到底是什么,我会先检查一下,看看它是你的输出还是你的输入函数。
  • 我进行了编辑以将输入与输出分开。如果 getchar() 是问题所在,如何在保持 while() 循环的同时替换它?

标签: c printf scanf


【解决方案1】:

替换:

int i = 0;

while ((c = getchar()) != EOF)
{
    scanf("%s %d\n", myStates[i].name, &myStates[i].population);
    i++; 
}

与:

int i;

for (i = 0; i < MAX_STATES; i++)
{
    if (scanf("%3s %d", myStates[i].name, &myStates[i].population) != 2)
        break;
}

这可以防止您进入太多状态,使用for 循环来放置循环控件,检测格式问题,防止字符串缓冲区溢出,并将第一个字符读入名称中。此外,如果输入是以交互方式输入的,则格式字符串中的尾随空格(例如空格或换行符)在scanf() 格式字符串中是一个非常糟糕的主意。如果输入来自文件,则不那么严重,但大多数时候仍然没有必要。 (有关更多信息,请参阅Trailing blank in scanf() format。)

保持一个while循环

如果你真的坚持需要while 循环,那么你可以使用:

int i = 0;

while (i < MAX_STATES && (c = getchar()) != EOF)
{
    ungetc(c, stdin);
    if (scanf("%3s %d", myStates[i].name, &myStates[i].population) != 2)
        break;
    i++; 
}

或:

int i = 0;

while (i < MAX_STATES && (c = getchar()) != EOF)
{
    myStates[i].name[0] = c;
    if (scanf("%2s %d", &myStates[i].name[1], &myStates[i].population) != 2)
        break;
    i++; 
}

请注意,这些while 循环仍然保持大量溢出保护——溢出主数组和溢出名称字段。请注意,两个scanf() 语句之一使用%3s,另一个使用%2s;你应该能够解释为什么。 (是的,scanf() 不计算空字节,因此您必须在转换规范中使用“减一”长度。)

毫无疑问,还可以使用其他技术。不过,我想您会发现for 循环更接近于惯用的 C。

通常明智的一种替代方法是使用fgets()(或POSIX getline(),如果可用)读取整行,然后使用sscanf() 解析行。这通常会导致更有弹性的程序和更好的错误报告。它还可以阻止那些试图将所有 50 个州的信息放在一行中,或者将每个数据放在单独的一行中并在它们之间用空行隔开的人逃脱格式错误的数据。你可以悄悄地坚持​​两个字段(如果你小心的话,只有两个字段)就行了。

输出代码呢?

我可以询问有关正确显示输出的建议吗?

你有:

printf("");

if (c <= 0)
{
    for(int j = 0; j <= MAX_STATES; j++)
    {
        if(myStates[j].population >= 10)
            printf("%s %d\n", myStates[j].name, myStates[j].population);
        else
            break;
    }
}

第一个printf() 什么都不做;它应该去。 if (c &lt;= 0) 条件有点可疑。可以输入一个空字节(通常是 Control-@Control-Shift-2),但要打破原始循环有点困难. for 循环应该更像for (int j = 0; j &lt; MAX_STATES; j++) — 这是 C 中安全 for 循环的模板。您最常使用 for (int i = 0; i &lt; MAX; i++)。但是,您只想打印已读取的状态,因此您需要使用i 作为限制,而不是使用MAX_STATES。如果您真的只想打印前 9 个州(加利福尼亚州、德克萨斯州、佛罗里达州、纽约州、伊利诺伊州、宾夕法尼亚州、俄亥俄州、乔治亚州、北卡罗来纳州 - 请参阅 Wikipedia;它说密歇根州只差 10M),那么 @987654352 @条件很好。

因此,您可以使用(注意输入循环将i 设置为成功读取的状态数):

for (int j = 0; j < i; j++)
    printf("State: %.2s, Pop'n: %dM\n", myStates[j].name, myStates[j].population);

当然,您可以调整格式以满足您的要求。如果没有读取任何状态或读取的状态数,这将不打印任何内容。如果您真的想将条件应用于人口,那么您可以使用:

for (int j = 0; j < i; j++)
{
    if (myStates[i].population >= 10)
        printf("State: %.2s, Pop'n: %dM\n", myStates[j].name, myStates[j].population);
}

【讨论】:

  • 抱歉,我编辑了原始帖子以反映“scanf()”而不是 fscanf()。除此之外,我需要使用 while() 循环,因为我正在从标准输入读取多行用户输入数据,当输入 EOF 标志 (ctrl-d) 时结束。然后,输出被吐出到标准输出。
  • 这段代码处理 EOF(通过测试来自scanf() 的返回值——现在我已经解决了这个问题),并且还通过不加载超出数组容量的行来防止溢出。使用%3s 可以确保如果有人写Texas 而不是TX,那么事情就不会出问题(但它允许CalCA 用于加利福尼亚)。
  • 非常感谢!所以我不得不重新调整我的输出以正确响应 scanf()。现在的输出看起来好多了!
  • 我可以询问有关正确显示输出的建议吗?我在类似的 for 循环中尝试了 if-else,它一直有效,直到我达到不满足 10 下限的条件并且它跳出循环。如果我没有 else --> break,最后我会得到一个疯狂的垃圾输出。
  • @DavidBowling:谢谢。 scanf() 格式的尾随换行符是个坏主意。我已经删除了它们并对此发表了评论。稍后我将添加一个指向规范问答的链接。 (感谢其他编辑。)
【解决方案2】:

另一种选择是:

int i = 0;
char temp[100];
for(i=0; i<MAX_STATES; i++){
    fgets(temp, 100, stdin);
    if(strcmp(temp, "\n") == 0)
        break;
    sscanf(temp, "%s %d\n", myStates[i].name, &myStates[i].population);
}

【讨论】:

  • 你需要测试来自sscanf()的返回值。不过,否则很好。
【解决方案3】:

您可以尝试在 scanf 中的 %s 之前添加一个空格,或者指定严格的字符数。

scanf(" %3s",

甚至可以像Beej's guide:一样使用

// read all whitespace, then store all characters up to a newline 

scanf(" %[^\n]", s);

【讨论】:

  • 请注意名称是char name[4];,因此在scanf() 转换规范中使用%3s 至关重要——scanf() 不计算终止的空值,但会加一。
【解决方案4】:

您可以尝试在 scanf 中的 %s 之前添加一个空格,或者指定严格的字符数。

或者甚至使用这个:

//读取所有空格,然后将所有字符存储到换行符

scanf(" %[^\n]", s);

【讨论】:

  • 没有必要在%s 指令和scanf() 之前添加前导空格字符,因为该指令会自动跳过前导空格。在%[] 指令之前添加它确实有意义,它不会忽略前导空白字符。尽管如此,这并不能解决 OP 的问题,即这一行中的 getchar()while ((c = getchar())!= EOF) {}
猜你喜欢
  • 2021-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-11
  • 2018-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多