【问题标题】:How to input a string with unknown size如何输入大小未知的字符串
【发布时间】:2018-11-02 09:54:44
【问题描述】:

我对 C 中的字符串有点困惑。我知道声明缓冲区大小很重要,否则会导致缓冲区溢出。但我需要知道如何获取我不知道大小的字符串输入。例如,如果我想从用户那里获取一行文本作为输入,但我无法知道他们的文本会有多长,我该怎么做?

我尝试在用户输入时动态分配内存。这是代码-

#include<stdio.h>
#include<stdlib.h>

int main()
{
    char *str, ch;
    int size = 10, len = 0;
    str = realloc(NULL, sizeof(char)*size);
    if (!str)return str;
    while (EOF != scanf_s("%c", &ch) && ch != '\n')
    {
        str[len++] = ch;
        if (len == size)
        {
            str = realloc(str, sizeof(char)*(size += 10));
            if (!str)return str;
        }
    }
    str[len] = '\0';
    printf("%s\n", str);
    free(str);
}

问题是,当我使用 VS-2017 编译它时,我得到了这些错误-

source.c(10): 警告 C4473: 'scanf_s' : 没有足够的参数传递 对于格式字符串

source.c(10): 注意:占位符及其参数需要 2 可变参数,但提供了 1 个

source.c(10):注意:缺少的可变参数 2 是必需的 格式化字符串'%c'

source.c(10): 注意:此参数用作缓冲区大小

我认为动态分配内存(如上面的代码)应该可以工作,但我可能做错了什么。有没有办法让这个工作?

编辑:字。

【问题讨论】:

  • 此处不要使用scanf,而应使用getc
  • 并且不要使用 C 标准附录 K 中的 scanf_s() 或任何其他 *_s() 函数。如果正确使用标准的 C 函数,它们并不比标准 C 函数更安全,and the *_s() functions are not portable as implemented by Microsoft:“由于与规范存在大量偏差,因此不能认为 Microsoft 实现符合或可移植。”

标签: c visual-studio-2017 scanf


【解决方案1】:
  1. 你应该使用getchar 而不是scanf_s
  2. 对于EOF,您应该使用int ch; 而不是char ch;

以下code 可以工作:

#include<stdio.h>
#include<stdlib.h>

int main() {
    char *str = NULL;
    int ch;
    size_t size = 0, len = 0;

    while ((ch=getchar()) != EOF && ch != '\n') {
        if (len + 1 >= size)
        {
            size = size * 2 + 1;
            str = realloc(str, sizeof(char)*size);
        }
        str[len++] = ch;
    }
    if (str != NULL) {
        str[len] = '\0';
        printf("%s\n", str);
        free(str);
    }

    return 0;
}

【讨论】:

  • sizelen 应该是 size_t 而不是 intrealloc() 也可能会失败。
  • @Yunbin Liu 你能告诉我我们为什么要检查 len+1 >= size 以及为什么我们要更新 size = size *2 + 1。请告诉我我无法理解那部分
【解决方案2】:
  • scanf_s 需要缓冲区大小作为参数,而您不希望这样
  • 一般情况下,scanf 会从 stdin 读取,一旦 \n 无论如何都会被输入。
  • 在这里使用getchar 是一种更好的方法

这是一个使用getchar的版本:

#include <stdio.h>
#include <stdlib.h>

int     main()
{
  char  *str = NULL;
  int   ch;
  int   capacity = 10, size = 0;

  str = realloc(str, sizeof(*str) * (capacity + 1));
  if (!str) return 1;
  while ((ch = getchar()) != EOF && ch != '\n')
    {
      if (size == capacity)
      {
        capacity += 10;
        str = realloc(str, sizeof(*str) * (capacity + 1));
        if (!str) return 1;
      }
      str[size] = (char)ch;
      size++;
    }
  str[size] = '\0';
  printf("%s\n", str);
  free(str);
  return 0;
}

【讨论】:

  • 挑剔:sizeof(char) 根据定义等于1,所以放弃它。或者,如果您打算提供对 str 类型更改具有鲁棒性的代码,请改用 sizeof *str
  • 第一次调用realloc(str) 可能有问题,因为str 从未初始化。此外,size == capacity 还不够好,因为您需要为结尾的 nul 字符留出额外的空间。
  • 是的,对不起,我错过了那部分,尽管它仍然会因为空字符串而失败,即当你从未进入循环时。
【解决方案3】:

scanf_s 需要为每个格式化输入占位符附加一个“缓冲区大小”参数。这就是您的编译器所抱怨的。所以当使用scanf_s 时,你必须写scanf_s("%c", &amp;ch, 1)。但是您也可以简单地使用scanf,因为您可以保证您的缓冲区ch 在任何情况下都足够大以接收字符值。所以scanf("%c",&amp;ch) 也可以安全地工作。

进一步注意scanf(和scanf_s)返回正确读取的值的数量; 0 的返回值也是可能的,这也表明没有读入任何内容。

所以我将它测试为 ...

while (scanf("%c", &ch)==1 && ch != '\n') 

【讨论】:

    【解决方案4】:

    您的代码几乎没问题。

    有两点需要改变:

    1. 不要使用scanf,而是使用getc
    2. ch应该是int,否则和EOF比较会出问题。
    3. 挑剔细节:main 应该在成功的情况下返回 0,在失败的情况下返回一个不同于 0 的值。

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
      char *str;
      int ch;
      int size = 10, len = 0;
    
      str = realloc(NULL, sizeof(char)*size);
      if (str == NULL)
        return 1;
    
      while ( (ch = getc(stdin)) && ch != EOF && ch != '\n')
      {
        str[len++] = (char)ch;
    
        if (len == size)
        {
          str = realloc(str, sizeof(char)*(size += 10));
    
          if (str == NULL)
            return 1;
        }
      }
    
      str[len] = '\0';
      printf("%s\n", str);
      free(str);
    }
    

    【讨论】:

      【解决方案5】:

      问题是,当我使用 VS-2017 编译它时,我得到了这些错误-

      source.c(10): warning C4473: 'scanf_s' : not enough arguments passed for format string
      
      source.c(10): note: placeholders and their parameters expect 2 variadic arguments, but 1 were provided
      
      source.c(10): note: the missing variadic argument 2 is required by format string '%c'
      
      source.c(10): note: this argument is used as a buffer size
      

      警告和注释非常明确,不是吗?

      在“备注”部分下查找the related documentation

      scanfwscanf 不同,scanf_swscanf_s 需要为所有输入参数指定缓冲区大小cCsS 类型或包含在 [ 中的字符串控件集]。以字符为单位的缓冲区大小作为附加参数传递,紧跟在指向缓冲区或变量的指针之后。

      所以你想改变

        while (EOF != scanf_s("%c", &ch) && ch != '\n')
      

      至少看起来像这样:

        while (EOF != scanf_s("%c", &ch, sizeof ch) && ch != '\n')
      

      更好的是提供完整的错误检查,如下所示:

        {
          int result;
          while (1 == (result = scanf_s("%c", &ch, sizeof ch)) 
                 && ch != '\n')
          {
            ...
          }
      
          if (EOF == result && ferror(stdin)) /* As scanf'ing uses stdin, 
                                                 test its error status. */
          {
            fprintf(stderr, "scanf_s() failed.\n")
          }
        }
      

      【讨论】:

        猜你喜欢
        • 2020-09-25
        • 1970-01-01
        • 1970-01-01
        • 2018-03-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-04
        相关资源
        最近更新 更多