有没有一种函数可以从标准输入中获取无限长度的输入字符串?

10

问题如下:

我想从标准输入中输入一行,但我不知道它的大小,可能非常长。

scanfgets 这样的方法需要知道您可能输入的最大长度,以便您的输入大小小于缓冲区大小。

那么有没有好的方法来处理它呢?

答案必须只用 C 语言,不能用 C++,因此 C++ 字符串不是我想要的。我想要的是 C 标准字符串,类似于 char* 并以 '\0' 结尾。


也许有什么可以根据您的输入大小自动分配内存的C语言工具吗? - Daizy
1
很少有用处的是这种功能。更常见的是,处理无限量输入的代码会发生一些输入被处理,然后又有更多输入等情况。例如使用真实的用户输入,如人名、电话号码或书名,当然会有100个字符长的姓名/书名和几十位数字的电话号码。但是,允许无限制的输入会诱使黑客使用10亿个字符长的名称/标题等来淹没系统。最好允许异常甚至病态的长输入,但不要无限制地允许。 fgets(user_name, 1000, stdin) 就足够了。 - chux - Reinstate Monica
1
gets不允许您指定目标数组的大小。这就是它固有的不安全性所在,这也是为什么它在2011年ISO C标准中被移除的原因。您是不是想使用fgets - Keith Thompson
读取未知数量的数据的方式强烈依赖于读取器如何处理数据。您可能想提供有关此的任何详细信息吗? - alk
5个回答

9

C标准没有定义这样的函数,但是POSIX有。

getline函数可以实现你所需的功能,文档在此(如果你使用的是类UNIX系统,可以输入man getline查看)。

它可能在非POSIX系统(如MS Windows)上不可用。

下面是一个演示其用法的小程序:

#include <stdio.h>
#include <stdlib.h>
int main(void) {
    char *line = NULL;
    size_t n = 0;
    ssize_t result = getline(&line, &n, stdin);
    printf("result = %zd, n = %zu, line = \"%s\"\n", result, n, line);
    free(line);
}

fgets 类似,'\n' 换行符会留在数组中。

5

一种方法是使用getchar运行循环,并将字符放入数组中。一旦数组已满,使用realloc来增大它的大小。


1
酷!有没有C库函数实现它? - Daizy
2
@youKnowDai 兄弟,这是 C 语言,你需要自己完成所有的工作并创建库。 - Bhargav Rao
1
标准C中没有getch()函数。你可能想用的是getc()或者getchar() - alk
是的,我的意思是getchar,已经修正了。谢谢你指出来! - Simba

1
其中一种方法是使用getchar()函数获取字符输入并将其传递给动态创建的数组。当它超出我们设置的默认长度时,我们会重新分配用于存储字符的空间。
#include<stdio.h>
#include<stdlib.h>

void main(){
    int size = 10;
    char* str;
    str = (char*) calloc(size,sizeof(char));
    char c;
    c = getchar();
    int t = 0;
    int cnt = 0;
    int len;

    while(c!='\n') {
        if(cnt > size) {
            str = (char*) realloc(str,2*cnt);
        }

        str[t] = c;
        c = getchar();
        t++;
        cnt++;
    }
        str[t]='\0';
    printf("The string is %s\n",str);
    len = strlen(str);
    printf("The size is %d",len);
}

0

scanf中经常被忽视的一个转换说明符可以分配足够长的内存来保存字符串输入,而不受长度限制。较新版本的scanf使用m来实现此目的。 早期版本使用a。例如:

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

int main (void) {
    char *str = NULL;
    printf (" enter a string of any length, whitespace is OK: ");
    scanf ("%m[^\n]%*c", &str);
    printf ("\n str: %s\n\n", str);
    if (str) free (str);
    return 0;
}

注意:scanf需要一个char **指针参数来接收分配的字符串。此外,请注意,scanf不会在存储的字符串中包含'\n'。进一步注意%*c,它接收并丢弃'\n'字符,以防止换行符留在输入缓冲区中。您还可以在转换说明符之前加上空格,以跳过可能存在于输入缓冲区中的任何/所有空格。

最后注意:有报告称,并非所有的scanf实现都提供此功能。(这也可能是m/a更改的混淆)请检查您的实现。


这种对 'm' 的使用是由 POSIX 定义的,但不是由 ISO C 定义的。 - Keith Thompson
没错,而且在 'm' 之前是 'a',所以你必须查看你的 scanf 版本的 man page -- 很好的观点。 - David C. Rankin
@NehalSamee 你在用哪个编译器? - David C. Rankin
嗯...是的...但在Windows上使用MinGW或Cgywin编译器。或者在Linux上使用gcc或clang。我不相信MinGW版本支持它。如果您已经安装了带有编译器的C::B,则很可能是TDM-MinGW,这是一个很好的编译器,但对m修饰符的支持存疑。 - David C. Rankin
要么声明一个足够大的缓冲区来容纳输入并逐个添加字符,要么使用malloc为缓冲区分配存储空间,并跟踪您添加的字符数,当达到分配的限制时,使用realloc将缓冲区大小加倍并继续添加。 - David C. Rankin
显示剩余2条评论

0

使用 getcharmallocrealloc 读取无限输入字符串。

声明 String 类型,也可以使用 char *

// String type
typedef char *String;

我写了这个函数来将字符连接到字符串的末尾。
/**
 * Join the Char into end of String
 *
 * @param string - String
 * @param c - joined char
 */
void String_joinChar(String *string, const char c)
{
  const size_t length = strlen(*string);
  (*string) = (String)realloc((*string), sizeof(char) * (length + 2));
  (*string)[length] = c;
  (*string)[length + 1] = '\0';
}

这个函数用于输入字符串,它通过使用getchar从键盘读取字符,并将其连接到当前字符串的末尾。

/**
 * Input String
 *
 * @return Inputed String
 */
String String_input()
{
  String string = (String)malloc(sizeof(char));
  strcpy(string, "");

  char cursor;
  fflush(stdin);
  while ((cursor = getchar()) != '\n' && cursor != EOF)
  {
    String_joinChar(&string, cursor);
  }

  return string;
}

由于使用了char *mallocrealloc,我们必须释放它。
/**
 * Destroy String
 *
 * @param string - Destroyed String
 */
void String_destroy(String string)
{
  free(string);
}

现在我们只需要使用它!!

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

int main()
{
  String string = String_input();
  printf("\n%s\n", string);
  String_destroy(string);
  return 0;
}

希望对你有用!


从实际角度来看,将大小为X的数组重新分配为大小为X + 2是低效的 - 您将在每个第二个输入字符上重新分配整个数组。更好的方法是重新分配到大小为X * 2。这样,您将达到常数摊销渐近时间复杂度,而不是插入操作进入动态数组的线性复杂度。 - Fanick
@Fanick 你是对的,如果如此,它将达到常数摊销渐近时间复杂度,但如果输入序列包含无限字符,它将遇到所谓的随时间的内存消耗。无限* 2 = 超级无限:))哈哈哈 - Daomtthuan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接