【问题标题】:How to make string function with user input in C?如何在 C 中使用用户输入创建字符串函数?
【发布时间】:2018-09-21 10:32:01
【问题描述】:

我知道如何使用 int、double、float 和内部用户输入来制作函数(我目前正在使用 scanf)。

int getData(){
    int a;
    scanf("%i",&a);
    return a;
}

但是如何用字符串类型和用户输入在里面创建函数,然后我们用字符串类型返回那个值呢?

【问题讨论】:

  • 在 C 中“返回字符串”的方法不止一种。
  • 我知道基本回报,但内部没有用户输入。如果我们处理用户输入,我目前会感到困惑。
  • 不,这是不同的问题。这个关于函数内部字符串和用户输入的问题,并返回它的值。这就是关于阅读字符串的话题。
  • 上面链接的问题标题不好,实际上是关于为什么将buffer而不是&buffer传递给scanf,而通常你有&integer等。不要使用此处显示的代码作为示例,说明如何使用 scanf 读取字符串 - 实际上,您必须防止缓冲区溢出,但该问题(和答案)省略了该部分,因为它不是与实际问题相关。

标签: c string function input


【解决方案1】:

C 字符串是一个由 NUL(零)字节终止的 char 数组。数组通常作为指向第一个元素的指针传递。从函数返回的问题是指向的地址必须在函数的生命周期之后保持有效,这意味着它需要是 static 缓冲区(然后被对同一函数的任何后续调用覆盖,破坏较早的返回值)或由函数分配,在这种情况下调用者负责释放它。

您提到的scanf 对于读取交互式用户输入也有问题,例如,它可能会使输入处于意外状态,例如当您不使用行尾的换行符时 next 调用scanf(可能在不相关的函数中)在遇到换行符时可能会出人意料地无法给出预期的结果。

将输入逐行读入缓冲区通常更简单,例如使用fgets,然后从那里解析行。 (有些输入你可以简单地通过逐个字符读取来解析没有缓冲区的输入,但是这样的代码通常会变得很长并且很难快速理解。)

读取任何可能包含换行符以外的空格的字符串的示例如下:

/// Read a line from stdin and return a `malloc`ed copy of it without
/// the trailing newline. The caller is responsible for `free`ing it.
char *readNewString(void) {
    char buffer[1024];
    if (!fgets(buffer, sizeof buffer, stdin)) {
        return NULL; // read failed, e.g., EOF
    }
    int len = strlen(buffer);
    if (len > 0 && buffer[len - 1] == '\n') {
        buffer[--len] = '\0'; // remove the newline
        // You may also wish to remove trailing and/or leading whitespace
    } else {
        // Invalid input
        //
        // Depending on the context you may wish to e.g.,
        // consume input until newline/EOF or abort.
    }
    char *str = malloc(len + 1);
    if (!str) {
        return NULL; // out of memory (unlikely)
    }
    return strcpy(str, buffer); // or use `memcpy` but then be careful with length
}

另一种选择是让调用者提供缓冲区及其大小,然后在成功时返回相同的缓冲区,在失败时返回NULL。这种方法具有 优点是调用者可以决定何时重用缓冲区以及是否需要复制字符串或只需读取一次并忘记。

【讨论】:

    【解决方案2】:

    Arkku 的方法扩展到无限大小(实际上它仅限于SIZE_MAX - 1 个字符)作为输入:

    #include <stdlib.h>
    #include <string.h>
    
    #define BUFFER_MAX (256)
    
    int read_string(FILE * pf, char ** ps)
    {
      int result = 0; 
    
      if (!ps)
      {
        result = -1;
        errno = EINVAL;
      }
      else
      {
        char buffer[BUFFER_MAX];
        size_t len = 0;
    
        *ps = NULL;
        while (NULL != fgets(buffer, sizeof buffer, pf))
        {
          len += strlen(buffer);
    
          {
            void * p = realloc(*ps, len + 1);
            if (!p)
            {
              int es = errno;
              result = -1;
              free(*ps);
              errno = es;
              break;
            }
    
            *ps = p;
          }
    
          strcpy(&(*ps)[len], buffer);
        }
    
        if (ferror(pf))
        {
          result = -1;
        }
      }
    
      return result;
    }
    

    这样称呼它:

    #include <stdlib.h>
    #include <stdlio.h>
    
    int read_string(FILE * pf, char ** ps);
    
    int main(void);
    {
      char * p;
    
      if (-1 == read_string(stdin, &p)) /* This read from standard input, 
                                           still any FILE* would do here. */
      {
        perror("read_string() failed");
        exit(EXIT_FAILURE);
      }
    
      printf("Read: '%s'\n", p);
    
      free(p); /* Clean up. */
    }
    

    【讨论】:

    • 很好地演示了realloc 的使用,但对于从stdin 读取用户输入的特定情况,我会选择一个相当小的最大支持行长度并且超出该长度会失败,而不是允许程序将(几乎)任意长的字符串读入内存。 =)
    • @Arkku:谢谢。 “我会选择...” 为什么?标准输入可以被重定向...
    • 因为正在读取的内容绝不是“没有换行符或 NUL 字节的任意字符串”,而是诸如“用户名”、“URL”、“从给定选项中选择”或“shell 命令”之类的内容, 所有这些都具有比SIZE_MAX 小几个数量级的最大长度。因此,虽然 支持 库函数中的任意上限很好,但我更希望能够提供特定于调用的最大值,而不是浪费内存和 CPU 从重定向stdin 只是为了在返回后拒绝它。最好早点失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-27
    • 2012-10-05
    • 2012-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-22
    相关资源
    最近更新 更多