【问题标题】:C Console InputC 控制台输入
【发布时间】:2016-01-25 22:22:12
【问题描述】:

我似乎无法用谷歌搜索,因为所有内容都显示为 C++ 或 C#(旁注:搜索特定于 C 的任何简单方法?)。我想弄清楚的是如何以我知道它的长度的方式接受控制台字符串输入,这样我就可以通过使用 for 循环向后索引它来以相反的顺序返回它。我过去有一点 C++ 经验,但从未真正使用过控制台 IO。任何帮助表示赞赏,谢谢。

【问题讨论】:

  • 在搜索中使用 c 标签来缩小结果范围,例如搜索"[c] console input"
  • fgets() 后跟 strlen() 是好的开始。
  • Google 最热门的点击最有可能出现在 StackOverflow 上。 ;-)
  • 我通常使用 K&R 的附录,它列出了所有的标准库函数。我还发现cplusplus.com/reference/clibrary 相当可靠。
  • POSIX 具有 getline 功能,它似乎可以满足您的要求:如果缓冲区对于当前行来说太短,它会自动扩大缓冲区。

标签: c string input


【解决方案1】:
  1. 阅读fgets()
  2. 处理可能的尾随\n
  3. 查找长度
  4. 反向打印。

    char buf[100];
    if (fgets(buf, sizeof buf, stdin) == NULL) Handle_EOF();
    buf[strcspn(buf, "\n")] = '\0';  // lop off potential trailing \n
    size_t len = strlen(buf);
    while (len) {
      putc(buf[--len], stdout);
    }
    

【讨论】:

  • 你还像在 C++ 中那样使用 int main(int argc, char *argv[]) {} 吗?
  • @Austin 以上只是一段代码。它仍然需要在int main(void) { 之类的函数中进行编码
  • 我只是尝试将它放在一个主块中并收到大量警告和链接器命令失败错误。不确定这是否是它的预期用途。
  • @Austin 您还需要确保您的#include <stdio.h>#include <string.h> 仍然...
  • @Austin" 大量警告" 含糊不清。 Handle_EOF(); 只是待定代码的占位符。如果stdin 已关闭,您希望代码做什么? #include <stdio.h> 和任何其他包含文件也未发布。听起来您正在寻找一个完整的可编译答案。我把基本的事情留给你完成。
【解决方案2】:

您可以使用fgets 函数从标准输入中读取数据。

char buf[80];

if (fgets(buf, 80, stdin) != NULL)
    /* buf now contains the first 80 chars of the input */

注意:请不要使用gets,因为它很危险——它会溢出输入缓冲区。

【讨论】:

    【解决方案3】:

    您需要留出一些空间来存储输入;由于您不提前知道输入有多大,因此您必须对存储进行一些创意。

    一个常见的策略是使用一个较小的、固定大小的缓冲区从输入流中读取,并使用一个动态的、可调整大小的缓冲区来存储完整的字符串,如果它最终比固定大小的缓冲区可以处理的长度更长。这样,您可以以离散的块读取任意长的输入行,然后将这些块粘贴在一起,并根据需要调整目标缓冲区的大小。

    您将从控制台循环读取固定大小的块并将其存储到动态缓冲区,直到您看到换行符,此时您退出输入循环。理想情况下,您的固定大小缓冲区应该足够大以处理最合理的情况,这样您就不需要扩展动态缓冲区。

    冗长(未经测试!)示例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define INPUT_BUF_SIZE 21 // handle strings up to 20 characters in length
    
    int main( void )
    {
      /**
       * Set aside a fixed-size buffer to store input from the console.  This
       * buffer cannot be resized after it has been allocated.
       */
      char inputBuf[INPUT_BUF_SIZE]; 
    
      /**
       * Dynamically allocate a buffer to store the final string; 
       * its initial size is the same as the fixed buffer.  If necessary,
       * this buffer may be extended with the realloc function.  We're using
       * calloc instead of malloc to make sure the initial array contents
       * are set to 0.
       */
      char *finalBuf = calloc( INPUT_BUF_SIZE, sizeof *finalBuf ); 
    
      /**
       * finalBufSize tracks the total size of the dynamic buffer; finalBufLen
       * tracks the length of the string currently stored in the buffer.
       * These are not the same thing.  
       */
      size_t finalBufSize = INPUT_BUF_SIZE;
      size_t finalBufLen = 0; // initially no string stored in the buffer
    
      /**
       * Read from standard input into the fixed-size buffer; loop until
       * we see EOF or there's an error on input.
       */
      while ( fgets( inputBuf, sizeof inputBuf, stdin ) )
      {
        /**
         * If there isn't enough space left in finalBuf to store
         * the latest chunk, double the size of finalBuf.  This strategy
         * minimizes the number of times you have to call realloc (which
         * can be expensive).  
         */
        if ( strlen( inputBuf ) + finalBufLen > finalBufSize )
        {
          /**
           * *Always* assign the result of realloc to a temp variable; if the
           * call fails it will return NULL, and if you overwrite the value
           * of finalBuf with NULL, you'll lose your only reference to any
           * previously allocated memory.  
           */
          char *tmp = realloc( finalBuf, finalBufSize * 2 );
          if ( tmp )
          {
            finalBuf = tmp;
            finalBufSize *= 2;
          }
          else
          {
            /**
             * We can't extend the buffer anymore, so we'll exit the
             * loop and work with what we have.
             */
            fprintf( stderr, "Could not extend storage buffer, exiting input loop\n" );
            break;
          }
        }
    
        /** 
         * Append the input string to the target buffer.
         */
        strcat( finalBuf, inputBuf );
        finalBufLen = strlen( finalBuf );
    
        /**
         * Did we see a newline in the last input chunk?  If so,
         * remove that newline from the final string (unless you
         * want to include that in your reversal) and exit
         * the loop.
         */
        char *newline = strchr( finalString, '\n' );
        if ( newline )
        {
          *newline = 0; // overwrite the newline character with the string terminator
          break;
        }
      }
    

    此时,finalBuf 包含来自控制台的输入,您可以反转此字符串以进行输出。完成后,释放使用 free 函数分配的内存,如下所示:

    free( finalBuf );
    

    理想情况下,您应该将所有输入处理分离到它自己的函数中,但现在这已经足够说明问题了。

    【讨论】:

    • 很多 好主意,值得 +1。我不同意的 2 个高级问题:1)'\n' 的测试可以被前面嵌入的空字符所欺骗。我看不到使用fgets() 检测的好方法。 2) 无限制地增加缓冲区允许黑客利用。 IMO,动态增加缓冲区是个好主意,但需要一些上限来防止漏洞。
    • @chux:非常好的点。我承认我不经常做这类事情,也不会考虑这类问题。
    【解决方案4】:

    这是我的解决方案,但使用递归:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define N 20
    
    void reverseW(char *, int);
    
    int main()
    {
    char tmp[N], *string;
    
    printf("Type a String:\n");
    scanf("%s", tmp);
    
    string=(char*)malloc((strlen(tmp)+1)*sizeof(char));
    if (string==NULL)
    {
        printf("Error !\n");
        exit(0);
    }
    strcpy(string, tmp);
    
    reverseW(string, strlen(string));
    printf("\nThe reverse of %s is %s !\n", tmp, string);
    
    free(string);
    return 0;
    }
    
    void reverseW(char *word, int size)
    {
    char tmp;
    if (size>1)
    {
        tmp=word[0];
        word[0]=word[size-1];
        word[size-1]=tmp;
        reverseW(word+1, size-2);
    }
    return;
    }
    

    【讨论】:

    • scanf("%s", tmp); 就像gets(tmp)。两者都会导致输入过长的未定义行为,都应该避免。
    • 我看不到递归增加了什么。
    • 可以使用递归,但这几乎没有优势,并且使用了int 类型的限制signed 大小,而不是最适合数组的size_t。请参阅ref 了解一个好的方法。
    【解决方案5】:

    我编写了这个函数来将输入从标准输入到缓冲区,用于 uni 中的 cli 项目。

    按字符读取标准输入,没有缓冲区溢出。

    /*max line lenght*/
    #define CLI_MAX_CMD_LEN 1024
    
    /*get_line copies characters from stdin to buffer pointed by buf, stopping when a
      carriage return or newline is encountered. It returns -1 on errors, otherwise strlen count */
    int get_line(char *buf) {
        int i,c;
        for (i=0;i<CLI_MAX_CMD_LEN;i++) {
            c = getchar();
            switch (c) {
                /*if terminating char is found an error has occurred*/
                case '\0':
                    printf("found '\\0' instead of '\\n'\n");
                    return -1;
                case '\t':
                    /*show possible commands*/
                    autocomplete(buf);
                    break;
                case EOF:
                    /* prints the warning to stdout */
                    printf("End of file reached\n");
                    /* continue to execute following cases code */ 
                case '\n':
                case '\r':
                    buf[i] = '\0';
                    return i;
                default :
                    buf[i] = c;
            }
        }
    
        /*if end of buffer is reached put terminating char on last element
          means that an error has occurred*/
        buf[CLI_MAX_CMD_LEN] = '\0';
        return -1;
    }
    

    【讨论】:

    • 这与 fgets() 有何不同?
    • 看起来确实没有什么不同。它只是为了展示如何按字符获取输入字符,这基本上是 fgets 所做的。您还可以为特殊含义的字符添加一个案例..
    • 使用int ch 而不是char c 更好。 getchar() 通常返回 257 个不同的值:0-255 和 EOF。将该结果保存为 char 会失去将 EOF 与有效字符输入区分开来的能力。
    • 1) 而不是 `case -1:, code should use case EOF:. EOF` 是一些负值,可能不是-1。 2) 从case -1 跌至case '\n' 似乎是故意的。在这里发表评论将消除疑问。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-19
    • 2018-03-17
    相关资源
    最近更新 更多