首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C中任意长度的输入

C中任意长度的输入
EN

Code Review用户
提问于 2022-04-30 05:24:30
回答 1查看 333关注 0票数 4

scanf()fgets(),我们需要首先指定字符串输入的最大长度,因为输入受到限制,这不是很好。由于输入可能与[0,∞)不同,由于计算机的限制,输入永远不会到达∞,但可能非常大。

我在C++中见过,字符串输入没有限制。

代码语言:javascript
复制
std::string x;
std::cin >> x;

因此,为了在C中实现这个特性,我创建了一个函数,该函数从用户那里获取一个字符,并将其附加到缓冲区的最后一个位置,然后调用realloc()作为缓冲区。

最终代码

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

static inline void exit_heap_fail(const void *ptr)
{
    if (!ptr)
    {
        fprintf(stderr, "err: null-pointer\n");
        exit(EXIT_FAILURE);
    }
}

static char *input(void)
{
    char *ptr = calloc(2, sizeof(char)), ch;
    exit_heap_fail(ptr);
    size_t len = 0;
    while ((ch = getchar()))
    {
        if (ch == 10 || ch == 0)
            break;
        ptr[len++] = ch;
        ptr = realloc(ptr, len + 1);
        exit_heap_fail(ptr);
    }
    ptr[len] = 0;
    return ptr;
}

int main(void)
{
    puts("Enter:");
    char *data = input();
    printf("DATA = `%s`\n", data);
    free(data);
    return EXIT_SUCCESS;
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2022-04-30 13:10:43

文件端

当不读取任何内容时,代码将返回分配的""。这与只读取一个'\n'是没有区别的,因为它也返回""

要像其他标准输入函数一样工作,代码应该返回一些指示,比如NULL

  • 没有输入和文件结束报告。
  • 无论读取什么内容,都会发生输入错误。

无限环

当文件结束时,getchar()返回EOF,而代码永远不会退出循环。

代码语言:javascript
复制
// Infinite loop
while ((ch = getchar())) {
    if (ch == 10 || ch == 0)
        break;

冗余测试

对空字符进行两次代码测试。

257

getchar()返回257个不同的值(EOF和all unsigned char)。保存在char中会丢失信息。

使用int ch

重复调用以重新分配

realloc(ptr, len + 1);每次迭代都调用realloc()。一种更有效的通用方法将每个循环的大小大约加倍,并在最后进行适当的大小分配。

结束空字符

上的输入

这与fgets(), scanf()不同。

编辑

一些示例代码来说明这些想法,还有一些更多。只是轻微的测试。

标题:

代码语言:javascript
复制
/* 
 * Allocate as needed to form a string from user input.
 *
 * Return NULL on
 * * End of file with no input
 * * Input error
 * * Out of memory
 * Caller to use feof(), ferror() to distinguish.
 *
 * Otherwise return allocated buffer.
 * Caller to free buffer.
 */
char* line_alloc(void);

来源

代码语言:javascript
复制
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define LINE_ALLOC_MIN 63  /* Some power-of-2 minus 1 */

static char* line_alloc_helper(char *buf, size_t *len_max_ptr) {
  // Is input longer than supportable size?
  if (*len_max_ptr > SIZE_MAX / 2 - 1) {
    free(buf);
    return NULL;
  }
  size_t sz = *len_max_ptr + 1;  // Old buffer size
  sz = 2 * sz + 1;               // New buffer size
  if (sz < LINE_ALLOC_MIN) {
    sz = LINE_ALLOC_MIN;
  }
  char *buf_new = realloc(buf, sz);
  if (buf_new == NULL) {
    free(buf);
    return NULL;
  }
  *len_max_ptr = sz - 1;
  return buf_new;
}

char* line_alloc(void) {
  size_t len = 0;
  size_t len_max = 0;
  char *buf = NULL;
  int ch;
  bool nothing_read = true;

  while ((ch = getchar()) != EOF) {
    nothing_read = false;
    if (ch == '\n' || ch == '\0') { // Remove ch == '\0' if desired.
      break;
    }
    if (len >= len_max) {
      buf = line_alloc_helper(buf, &len_max);
      if (buf == NULL) {
        return NULL;
      }
    }
    buf[len++] = (char) ch;
  }

  if (ch == EOF) {
    if (nothing_read || !feof(stdin)) {  // Note 1
      free(buf);
      return NULL;
    }
  }

  char *buf_right_size = realloc(buf, len + 1);
  if (buf_right_size == NULL) {
    free(buf);
  } else {
    buf_right_size[len] = '\0';
  }
  return buf_right_size;
}

/* Note 1: Better as !feof() than ferror() to identify 
end-of-file with input error set from prior activity. */

测试:

代码语言:javascript
复制
#include <string.h>

int main(void){
    char * buf;
    while ((buf = line_alloc()) != NULL) {
        size_t len = strlen(buf);
        printf("%2zu <%s>\n", len, buf);
        free(buf);
        fflush(stdout);
    }
}

今后的改进/设想:

  • 支持FILE *input_stream
  • 最好使用本地缓冲区,比如127个字节,并在适当大小的步骤中对短行使用malloc()。转换到为长行分配的缓冲区。增加了复杂性,但只为公共行分配了一次。
  • 处理从读取外文文本文件中产生的'\r' '\n'对。
  • 为调用方提供len,以便更好地支持嵌入空字符的行。
  • 为最大行长提供上限而不是SIZE_MAX,以帮助防止黑客滥用代码。
票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/276173

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档