如何读取长度未知的输入字符串?

Posted

技术标签:

【中文标题】如何读取长度未知的输入字符串?【英文标题】:How can I read an input string of unknown length? 【发布时间】:2022-01-22 04:34:44 【问题描述】:

如果我不知道这个词有多长,我不能写char m[6];, 单词的长度可能是十或二十长。 如何使用scanf 从键盘获取输入?

#include <stdio.h>
int main(void)

    char  m[6];
    printf("please input a string with length=5\n");
    scanf("%s",&m);
    printf("this is the string: %s\n", m);
    return 0;

请输入长度=5的字符串 你好 这是字符串:你好

【问题讨论】:

使用指针重新分配组合 您可以在scanf("%s",&amp;m) 中删除&amp;,因为m 已经是指向此表达式中m[] 的第一个元素的指针。 【参考方案1】:

在动态保护区域的同时进入

例如

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

char *inputString(FILE* fp, size_t size)
//The size is extended by the input with the value of the provisional
    char *str;
    int ch;
    size_t len = 0;
    str = realloc(NULL, sizeof(*str)*size);//size is start size
    if(!str)return str;
    while(EOF!=(ch=fgetc(fp)) && ch != '\n')
        str[len++]=ch;
        if(len==size)
            str = realloc(str, sizeof(*str)*(size+=16));
            if(!str)return str;
        
    
    str[len++]='\0';

    return realloc(str, sizeof(*str)*len);


int main(void)
    char *m;

    printf("input string : ");
    m = inputString(stdin, 10);
    printf("%s\n", m);

    free(m);
    return 0;

【讨论】:

乘以sizeof(char)?呃。 @Jens Pfff,这可能会被优化掉。没问题。但是,如果您要使用 wchar_tchar 进行全局查找和替换,此解决方案仍然有效,与其他解决方案不同,需要更多修改! @MrLister 这就是为什么正确的方法(如果有的话)是与sizeof (*str) 相乘,因此当类型更改时您甚至不必编辑乘法. str[len]='\0'; return realloc(str, len); 肯定会导致终止符被丢弃。我想你的意思是str[len++] = '\0' @germanfr 不,我说的是realloc(NULL, sizeof(char)*size); the same as malloc(sizeof(char) * size)mallocrealloc 我不会这么说。【参考方案2】:

使用当今的计算机,您可以避免分配非常大的字符串(数十万个字符),而几乎不会减少计算机的 RAM 使用量。所以我不会太担心。

但是,在过去,当内存非常宝贵时,通常的做法是分块读取字符串。 fgets 从输入中读取最多字符数,但输入缓冲区的其余部分保持不变,因此您可以随意读取其余部分。

在这个例子中,我读取了 200 个字符的块,但是你当然可以使用任何你想要的块大小。

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

char* readinput()

#define CHUNK 200
   char* input = NULL;
   char tempbuf[CHUNK];
   size_t inputlen = 0, templen = 0;
   do 
       fgets(tempbuf, CHUNK, stdin);
       templen = strlen(tempbuf);
       input = realloc(input, inputlen+templen+1);
       strcpy(input+inputlen, tempbuf);
       inputlen += templen;
     while (templen==CHUNK-1 && tempbuf[CHUNK-2]!='\n');
    return input;


int main()

    char* result = readinput();
    printf("And the result is [%s]\n", result);
    free(result);
    return 0;

请注意,这是一个没有错误检查的简化示例;在现实生活中,您必须通过验证 fgets 的返回值来确保输入正确。

还要注意,如果在 readinput 例程的最后,没有字节被浪费;该字符串具有所需的确切内存大小。

【讨论】:

需要对返回 NULLrealloc() 进行错误处理(因此,对于返回 NULLreadinput())。 需要检查fgets()的返回值,否则代码可能进入死循环。 我认为在第一个 realloc 上存在问题(输入为 NULL 时的 realloc)。这可能指向任意内存,因此 strcat 可能没有预期的结果(即输入应该只是缓冲区的内容)。相反,它不会尝试存储已分配的长度为 Templen 的字符串,而是尝试存储 strlen(任意数据) + Templen 的字符串,并给出“malloc() 内存损坏”错误。 @BrendanHart Aw,六年来没有人看到这一点。通过执行 strcpy 而不是 strcat 修复。 要丢失换行符,请在fgets 行之后添加tempbuf[strcspn(tempbuf, "\n")] = 0;【参考方案3】:

我只见过一种 简单 读取任意长字符串的方法,但我从未使用过它。我觉得是这样的:

char *m = NULL;
printf("please input a string\n");
scanf("%ms",&m);
if (m == NULL)
    fprintf(stderr, "That string was too long!\n");
else

    printf("this is the string %s\n",m);
    /* ... any other use of m */
    free(m);

%s 之间的m 告诉scanf() 测量字符串并为其分配内存并将字符串复制到其中,并将该分配内存的地址存储在相应的参数中。一旦你完成了它,你必须free()它。

不过,scanf() 的每个实现都支持此功能。

正如其他人所指出的,最简单的解决方案是对输入的长度设置限制。如果你仍然想使用scanf(),那么你可以这样做:

char m[100];
scanf("%99s",&m);

注意m[] 的大小必须至少比%s 之间的数字大一个字节。

如果输入的字符串长于 99,那么剩余的字符将等待另一个调用或传递给scanf() 的其余格式字符串读取。

一般不建议将scanf() 用于处理用户输入。它最适用于由另一个应用程序创建的基本结构化文本文件。即便如此,您也必须意识到输入的格式可能与您期望的不一样,因为有人可能会干扰它以试图破坏您的程序。

【讨论】:

我原来也忽略了检查分配失败。 请注意,"%ms" 不是标准 C --- 它可能是 POSIX 扩展或 GNU 扩展。 @TimČas:它是 Posix 2008 的一部分,它是一个标准。早期有一个类似的 GNU 扩展和一个类似的 BSD 扩展; Posix 标准旨在统一各种实现。它很有可能会进入未来的 C 标准。【参考方案4】:

C 标准中有一个新函数可以在不指定其大小的情况下获取一条线。 getline 函数自动分配所需大小的字符串,因此无需猜测字符串的大小。下面的代码演示了用法:

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


int main(void)

    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    while ((read = getline(&line, &len, stdin)) != -1) 
        printf("Retrieved line of length %zu :\n", read);
        printf("%s", line);
    

    if (ferror(stdin)) 
        /* handle error */
    

    free(line);
    return 0;

【讨论】:

其实它不在C标准中,但在POSIX中确实存在,所以它的使用范围相当广泛【参考方案5】:

如果我可以建议一种更安全的方法:

声明一个足够大的缓冲区来保存字符串:

char user_input[255];

安全的方式获取用户输入:

fgets(user_input, 255, stdin);

获取输入的安全方法,第一个参数是指向将存储输入的缓冲区的指针,第二个是函数应读取的最大输入,第三个是指向标准输入的指针 - 即用户输入来自。

安全性尤其来自第二个参数,它限制了读取的数量,以防止缓冲区溢出。此外,fgets 负责处理的字符串以空值结尾。

有关该功能的更多信息here。

编辑:如果您需要进行任何格式化(例如,将字符串转换为数字),您可以在输入后使用atoi。

【讨论】:

但是 OP 问他不知道他要输入多少,如果他随机想输入 > 255 IMO fgets(user_input, sizeof user_input, stdin); 更安全。 @chux-ReinstateMonica 直到你使用指针而不是数组;) 但是假设你写了一个shell。您真的要限制为 255 或任何固定值吗?第一个答案似乎更擅长在编译时处理未知字符串 sized-string。【参考方案6】:

更安全、更快(容量翻倍)版本:

char *readline(char *prompt) 
  size_t size = 80;
  char *str = malloc(sizeof(char) * size);
  int c;
  size_t len = 0;
  printf("%s", prompt);
  while (EOF != (c = getchar()) && c != '\r' && c != '\n') 
    str[len++] = c;
    if(len == size) str = realloc(str, sizeof(char) * (size *= 2));
  
  str[len++]='\0';
  return realloc(str, sizeof(char) * len);

【讨论】:

通常提示是程序员提供的。所以你不能说它不安全。如果程序员在提示符中指定了一些格式说明符,这可能是不安全的。但我改变了它。 乘以sizeof (char) 是多余的; sizeof (char) 定义为 1。 prompt 应该是 const char * 您应该检查所有mallocrealloc 调用是否有错误。 未经检查的乘法size *= 2会溢出。【参考方案7】:

获取一个字符指针来存储所需的字符串。如果您对字符串的可能大小有所了解,请使用函数

char *fgets (char *str, int size, FILE* file);`

否则,您也可以使用动态提供请求内存的 malloc() 函数在运行时分配内存。

【讨论】:

【参考方案8】:

我知道我是在 4 年后到达的,但为时已晚,但我想我还有另一种方式可供他人使用。我曾经使用过getchar()这样的函数:-

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

//I had putten the main Function Bellow this function.
//d for asking string,f is pointer to the string pointer
void GetStr(char *d,char **f)

    printf("%s",d);

    for(int i =0;1;i++)
        
        if(i)//I.e if i!=0
            *f = (char*)realloc((*f),i+1);
        else
            *f = (char*)malloc(i+1);
        (*f)[i]=getchar();
        if((*f)[i] == '\n')
        
            (*f)[i]= '\0';
            break;
        
       


int main()

    char *s =NULL;
    GetStr("Enter the String:- ",&s);
    printf("Your String:- %s \nAnd It's length:- %lu\n",s,(strlen(s)));
    free(s);

这是该程序的示例运行:-

Enter the String:- I am Using Linux Mint XFCE 18.2 , eclispe CDT and GCC7.2 compiler!!
Your String:- I am Using Linux Mint XFCE 18.2 , eclispe CDT and GCC7.2 compiler!! 
And It's length:- 67

【讨论】:

【参考方案9】:

使用fgets() 直接读取分配的空间。

需要特别注意区分成功读取、文件结束、输入错误和内存不足。 EOF 需要适当的内存管理。

此方法保留一行的'\n'

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

#define FGETS_ALLOC_N 128

char* fgets_alloc(FILE *istream) 
  char* buf = NULL;
  size_t size = 0;
  size_t used = 0;
  do 
    size += FGETS_ALLOC_N;
    char *buf_new = realloc(buf, size);
    if (buf_new == NULL) 
      // Out-of-memory
      free(buf);
      return NULL;
    
    buf = buf_new;
    if (fgets(&buf[used], (int) (size - used), istream) == NULL) 
      // feof or ferror
      if (used == 0 || ferror(istream)) 
        free(buf);
        buf = NULL;
      
      return buf;
    
    size_t length = strlen(&buf[used]);
    if (length + 1 != size - used) break;
    used += length;
   while (buf[used - 1] != '\n');
  return buf;

示例用法

int main(void) 
  FILE *istream = stdin;
  char *s;
  while ((s = fgets_alloc(istream)) != NULL) 
    printf("'%s'", s);
    free(s);
    fflush(stdout);
  
  if (ferror(istream)) 
    puts("Input error");
   else if (feof(istream)) 
    puts("End of file");
   else 
    puts("Out of memory");
  
  return 0;

【讨论】:

错误:从‘void*’到‘char*’的无效转换 [-fpermissive]==> char *buf_new = realloc(buf, size); @Hani Goc 你用的是什么编译器? C 编译器还是 C++ 编译器?您的评论与使用 -fpermissive 的 C++ 编译器一致,但投诉的 C 编译器不会给出该消息,并且这篇文章被标记为 C 我正在使用 C++ 编译器。对不起,我有点困惑。我的错误【参考方案10】:

我也有标准输入和输出的解决方案

#include<stdio.h>
#include<malloc.h>
int main()

    char *str,ch;
    int size=10,len=0;
    str=realloc(NULL,sizeof(char)*size);
    if(!str)return str;
    while(EOF!=scanf("%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);

【讨论】:

scanf 的返回值应与您读取的元素数量进行比较,而不是 EOF

以上是关于如何读取长度未知的输入字符串?的主要内容,如果未能解决你的问题,请参考以下文章

从文件中读取未知长度的 int 数组

在C中动态分配的从文件读取的未知长度字符串(必须防止从文件中读取数字)

无法在Fortran中读取未知长度字符串的实数

如何使编码未知的字节序列可用作 PHP 的输入?

c语言中如何输入一个没有长度的数组

如何用bitset储存未知长度的序列?