【问题标题】:String Input with multiple lines多行字符串输入
【发布时间】:2018-08-02 19:14:44
【问题描述】:
int main() {

char userInput[100];    //Store user input

//Take user input
//scanf(" %s",&userInput);
//scanf("%[^\n]s",&userInput);
//scanf("%[^\n]", &userInput);
//gets(userInput);
scanf("%[]s", &userInput); //This takes input but doesnt leave input loop

printf(" %s",userInput);

//i = index to start for looping through the string, starting at the beginning
//count = Stores occurrences of '$'
//inputLength = length of the input, used for limit of loop
int i =0,count =0;
int inputLength = strlen(userInput);

//Loop through the user input, if the character is '$', the integer count will be incremented
for (i; i < inputLength; i++){
    if (userInput[i] == '$'){
        count++;
    }
}

printf("%d", count);
return 0;
}

您好,我的代码有一些问题,我需要输入 3 行并计算输入中“$”的数量。未注释的输入法 "scanf("%[]s", &userInput);" 是我发现的唯一一种可以输入所有 3 行输入的方法,但我无法打破输入循环并继续我的程序。

任何帮助将不胜感激

【问题讨论】:

  • 什么输入循环?
  • 不要使用scanf。
  • 你最好只使用fgets一次读取一行,并保持$的运行总数
  • 如果您的目标是确定前 3 行中$ 的数量,则一次读取一个字符。数一下$\n。当你读到第三个\n时,停下来。
  • 是的,@Tom,scanf 真是让人头疼。它很强大,但它有很多陷阱并且经常绊倒人们。陷阱之一是,如果您不小心,它很容易产生缓冲区溢出。你不小心。

标签: c input


【解决方案1】:

要使用繁琐的 scanf() 读取 3 ,代码需要查找 '$''\n'EOF。其余的输入是可丢弃的。

int count = 0;
int line = 0;
while (line < 3) {
  scanf("%*[^$\n]");  // Scan for any amount of characters that are not $ nor \n,
                      // "*" implies - do not save. 
  char ch;
  if (scanf("%c", &ch) != 1) {  // Read next character.
    break;
  }
  if (ch == '$') count++;
  else line++;
}

printf("$ count %d\n", count);

【讨论】:

  • 谢谢你的回复,你怎么能只有"scanf("%*[^$\n]");"而不将其存储在诸如 input[100] 之类的地方?
  • @tom 根据问题,不需要保存行,只需“输入 3 行并计算'$”的数量 - 与保存行无关。此外,由于没有规定行的最大长度的上限,编码到一些人为的上限可能无论如何都不能满足编码目标。 IAC,实现既定目标不需要最大线路长度。
  • 第一个带有break的if语句,这是否意味着如果字符是空格,请阅读下一行?
  • @Tom 研究scanf()scanf("%c", &amp;ch) 返回 1 表示成功扫描了 1 个字段(读取了一个字符,由于之前的 scanf("%*[^$\n]");,它必须是 '&amp;''\n')。否则返回 EOF(没有更多数据或罕见的输入错误),在这种情况下,此处的代码会简单地退出尝试读取,然后直接继续打印结果。
【解决方案2】:

正如@chux 建议的那样,使用fgets 读取提供了一种方便的方法来防止缓冲区溢出,并且不必在scanf 转换说明符中硬编码字段宽度修饰符。

在这里,如果您只需要计算输入中的'$' 字符数(无论有多少行),您就可以简单地以固定大小的数据块读取所有输入。 fgets 就是这样做的。如果您有一行或一百万行输入,这并不重要。您的输入行是一字符还是一百万字符也没有关系。您可以简单地读取每一行并计算在读取的每个数据块中找到的'$' 的数量,并记录找到的总数。

您可以对任何角色执行此操作。如果您还想计算行数,您可以简单地检查 '\n' 字符并在那里保持总数。使用fgets 计数行的唯一极端情况是确保您免受非POSIX 文件结尾 (意味着没有'\n' 作为最后一个字符的文件)。有几种方法可以处理这个问题。检查读取的最后一个字符是'\n' 和其他任何方法一样好。

将各个部分放在一起,并防止非 POSIX eof,您可以执行类似于以下操作的操作,它只是读取 stdin 上的所有可用数据并输出最终的 '$' 和行数:

#include <stdio.h>

#define MAXC 100

int main (void) {

    char buf[MAXC] = "";  /* buffer to hold input in up to MAXC size chunks */
    size_t lines = 0, dollars = 0;  /* counters for lines and dollar chars */
    int i = 0;

    while (fgets (buf, MAXC, stdin))    /* read all data */
        for (i = 0; buf[i]; i++)        /* check each char in buf */
            if (buf[i] == '$')          /* if '$' found */
                dollars++;              /* increment dollars count */
            else if (buf[i] == '\n')    /* if '\n' found */
                lines++;                /* increment line count */

    if (i && buf[i-1] != '\n')          /* protect against non-POSIX eof */
        lines++;

    /* output results */
    printf ("input contained %zu lines and %zu '$' characters.\n", 
            lines, dollars); 

    return 0;
}

查看一下,如果您还有其他问题,请告诉我。

【讨论】:

  • @chux 无意,size_t 很可能是unsigned long,但只是选择了size_t 作为计数类型——(它和unsigned long 在大多数平台上应该足够了) 将调整国旗。 (实际上只是重新设计了测试以测试最后一个字符读取)
  • 足够好 - 紫外线。请注意,fgets() Achilles heal 是在读取 null 字符 时。检测到这些的能力是我看到scanf() 的唯一优势。 "\0xyz" 的最后几行会欺骗您原本漂亮的行数保护。
  • @chux 现在你把我带到了那里。当我使用fgets 阅读nul-character 时,fgets 会读取一个 empty-string,然后在下一次迭代中抛出 EOF。这似乎是fgets 的致命弱点。我什至不确定是否值得尝试捕获这种情况。更多规则“如果嵌入的 nul-characters 可以出现在您的输入中 - 不要使用 fgets。”必须站起来……
  • @chux,我猜你可以捕获!i,例如if ((i &amp;&amp; buf[i-1] != '\n') || !i) 至少会将行数保留到遇到 nul-character 的点。
【解决方案3】:

scanf("%[]s", &userInput);" 是我发现的唯一一个接受所有 3 行输入的方法,但我无法中断输入循环并继续执行我的程序。

"%[]" 是无效的 scanf() 说明符。任何事情都可能发生,它是未定义的行为,包括将所有行放入而不返回。

格式中的's' 在这里没有用处 - 删除它。


是的fgets() 是最好的,但让我们滥用scanf() 阅读3 行 并寻找'$'

char line[3][100] = {0};
//                 v--------- Consume all leading whitespace 
//                 | vv ----- limit input to 99 characters as scan() appends a \0
//                 | || v-v-- Look for "not \n" 
#define FMT_1LINE " %99[^\n]"

// Let the compiler concatenate the 3 formats into 1 string for scanf
int scan_count = scanf(FMT_1LINE FMT_1LINE FMT_1LINE, line[0], line[1], line[2]);
// Check return value
if (scan_count == 3) {
  // Successfully read 3 lines
  int count = 0;
  for (int line_index = 0; line_index < 3; line_index++) {
    char *s = line[line_index];
    while (*s) {  // no need for strlen(), just loop until the null character
      count += *s == '$';
      s++;
    }
  }
  printf("$ count %d\n", count);
}

【讨论】:

  • @john " %99[^\n]" 使用前一行的换行符以及空格和空行。是的,这里没有规定 > 99 个字符的行或只有 "\n" 的行。
  • 回到我原来的评论,或多或少:如果其中一些不包含任何非空白字符,这可能会读到三行以上,如果有些超过 99 个字符,读起来可能会更少. scanf 可以用来做这个,但是比那个要麻烦很多。
  • @JohnBollinger 是的。我以后可能会尝试一个强大的版本。
  • @chux 感谢您的回复,它帮助我更多地理解了这个问题。
【解决方案4】:

你写:

scanf("%[]s", &userInput); //This takes input but doesnt leave input loop

但该评论充其量只是误导。您的格式字符串格式错误,因此 scanf 调用的行为未定义。一个空的扫描集(在格式中的[] 之间)没有意义,因为结果字段永远不会匹配任何内容。因此,在扫描集的开头] 之后立即出现的] 被解释为文字字符而不是结束分隔符。因此,您的扫描集未终止。

还要注意,%[ 是它自己的字段类型,与 %s 分开。扫描集的结束 ] 后面的 's' 不是此类字段描述符的一部分,而是要匹配的普通字符。

使用scanf 执行此操作的一种简单方法是通过%c 字段在循环中一次读取一个字符。这可能不是练习所要寻找的,为此目的使用scanf() 而不是getchar() 是一种技巧,但也许它会起作用:

int nl_count = 0;
int dollar_count = 0;

do {
    char c;
    int result = scanf("%c", &c);

    if (result != 1) {
        break;
    }
    switch (c) {
        case '\n':
            nl_count++;
            break;
        case '$':
            dollar_count++;
            break;
    }
} while (nl_count < 3);

恐怕使用%[ 字段一次安全地读取多个字符会更加复杂,并且没有安全的方法可以同时读取所有三行scanf 调用,除非您可以依靠输入行不超过您已知的行长度限制。

【讨论】:

    【解决方案5】:
    int readMatrix() {
    
    char userInput[100][3];    //Store user input
    int j = 0, m = 0;
    
    for(m = 0; m < 3; m++){
        scanf("%s", &userInput[j][m]); //This takes input (Ex: 22 *(enter)* 33$ *(enter)* 66$ *(enter)*
        j++; //increase the column
    }
    
    
    
    int i =0,count =0;
    m = 0;
    //Loop through the user input, if the character is '$', the integer count will be incremented
    for (i = 0; i < 100; i++){
        for(m = 0; m < 3; m++){
            if (userInput[i][m] == '$'){
                count++;
            }
        }
    }
    printf("%d", count);
    return 0;
    }
    

    【讨论】:

    • 很好地使用了循环,但scanf("%s", &amp;userInput[j][m])gets(&amp;userInput[j][m]) 相似,但缺乏缓冲区溢出保护。最好使用scanf("%99s", ...
    • if (userInput[i][m] == '$') 是代码读取过去userInput[i][m] == '\0' 时出现的问题。 “您的输入将始终为 100,因为您声明了 char userInput[100][3];”不正确。 scanf("%s", ... 不一定填满整个 100。
    • 感谢您的回复,如果您不介意,我有几个问题。在用于获取用户输入的第一个 for 循环中,我看到它如何从第一行循环到第三行,每次都获取输入并将其放入数组中,但是为什么循环末尾有 'j++'?
    猜你喜欢
    • 1970-01-01
    • 2023-04-08
    • 2013-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多