有什么简单的方法可以在 C 中读取可变长度的字符串吗?

Any simple way to read a string of variable length in C?

提问人:Prad 提问时间:1/28/2023 更新时间:1/28/2023 访问量:266

问:

我尝试使用以下方法阅读:

char *input1, *input2;
scanf("%s[^\n]", input1);
scanf("%s[^\n]", input2);

我显然做错了什么,因为第二个字符串被读取为 null。我知道不建议使用 scanf(),但我找不到任何其他简单的方法来做同样的事情。

C 字符串 IO 扫描

评论

0赞 Fe2O3 1/28/2023
在担心之前,仅仅有指向字符串的指针是不够的。您必须定义预期字符应驻留的空间(内存)。scanf()
5赞 Some programmer dude 1/28/2023
除了指针问题。该格式不会像您预期的那样工作。如果要阅读行,请改用。并且通常会忘记它的存在。 可以用来解析字符串,但通常会带来比它的价值更多的麻烦。%s[^\n]fgetsscanfsscanfscanf
1赞 Weather Vane 1/28/2023
格式和非常不同,具有不同的行为。一个不是另一个的子集。对于第二个,前一个终止换行符仍保留在缓冲区中。因此,您需要一个前导空格来过滤输入。请参阅 scanf() 将换行符字符保留在缓冲区中"%s""%[]""%[^\n]"" %[^\n]"
0赞 chux - Reinstate Monica 1/28/2023
Prad,输入的最大长度是多少?
0赞 Prad 1/28/2023
@WeatherVane 它是可变长度的(没有空格)。

答:

4赞 Harith 1/28/2023 #1

声明:

char *input1, *input2;

为指向 的两个指针分配内存。请注意,这只为该指针分配了内存,这些指针未初始化,不指向任何有意义的内容,而不是它们所指向的内容。char

然后,调用尝试越界写入内存,并导致未定义的行为。scanf()

相反,您可以声明具有自动存储持续时间的固定大小的字符数组:

char input1[SIZE];

这将为数组分配内存,并且调用将有效。scanf()

或者,您可以使用以下内存分配函数之一为指针动态分配内存:

char *input1 = malloc (size);

这将声明一个指针,其内容不确定,但会立即被指向大小为 的内存块的指针覆盖。请注意,调用可能失败。它以错误代码的形式返回,因此请检查它。charsizemalloc()NULL

但不应用作用户输入接口。它不能防止缓冲区溢出,并且会在输入缓冲区中留下一个换行符(这会导致更多的问题)。scanf()

请考虑改用。它将以 null 结尾缓冲区并读取大多数字符。fgetssize - 1

调用 to 可以替换为:scanf()

fgets (buf, sizeof buf, stdin);

然后,您可以使用 、 等解析字符串。sscanfstrtol

请注意,如果有空格,将保留尾随换行符。您可以使用此单行来删除它:fgets()

buf [strcspn (buf, "\n\r") = '\0`;

这也照顾了回程车厢(如果有的话)。

或者,如果您希望继续使用(我建议不要这样做),请使用字段宽度来限制输入并检查 的返回值:scanf()scanf()

scanf ("%1023s", input1); /* Am using 1023 as a place holder */

话虽如此,如果你想读取可变长度的行,你需要用 动态分配内存,然后根据需要调整它的大小。malloc()realloc()

在符合 POSIX 标准的系统上,您可以使用它来读取任意长度的字符串,但请注意,它容易受到 DOS 攻击。getline()

0赞 chqrlie 1/28/2023 #2

有 2 种简单的方法可以从输入流中读取可变长度字符串:

  • 与足够大的数组一起使用,以达到最大长度:fgets()
    char input1[200];
    if (fgets(input1, sizeof input1, stdin)) {
        /* string was read. strip the newline if present */
        input1[strcspn(input1, "\n")] = '\0';
        ...
    } else {
        /* nothing was read: premature end of file? */
        ...
    }
  • 在符合 POSIX 标准的系统上,您可以使用将任意长度的字符串读取到分配有以下值的数组中:getline()malloc()
    char *input1 = NULL;
    size_t input1_size = 0;
    ssize_t input1_length = getline(&input1, &input1_size, stdin);

    if (input1_length >= 0) {
        /* string was read. length is input1_length */
        if (input1_length > 0 && input1[input1_length - 1] == '\n') {
            /* remove the newline if present */
            input1[--input1_length] = '\0';
        }
        ...
    } else {
        /* nothing was read: premature end of file? */
        ...
    }

不建议使用,因为它很难正确使用,并且读取具有或没有指定最大长度的输入是有风险的,因为任何足够长的输入都会导致缓冲区溢出和未定义的行为。像在发布的代码中一样传递未初始化的指针具有未定义的行为。scanf"%s""%[^\n]"scanf

1赞 tstanisl 1/28/2023 #3

您可以使用修饰符来设置说明符的格式。请注意,它不是标准的 C,而是标准的 POSIX 扩展m

char *a, *b;

scanf("%m[^\n] %m[^\n]", &a, &b);

// use a and b
printf("*%s*\n*%s*\n", a, b);

free(a);
free(b);
0赞 Support Ukraine 1/28/2023 #4

有什么简单的方法可以在 C 中读取可变长度的字符串吗?

不幸的是,答案是否定的

C 标准指定的输入函数(例如 、 等)都需要调用方提供输入缓冲区。一旦输入缓冲区已满,函数将(正确使用时)返回。因此,如果输入长度超过提供的缓冲区的大小,则函数将仅读取部分输入。因此,调用方必须添加代码来检查部分输入,并根据需要执行其他函数调用。scanffgets

Posix系统具有可以做到这一点的功能。因此,如果您可以接受将代码限制为符合 Posix 的系统,那就是您想要使用的。getlinegetdelim

如果您需要可移植的、符合标准的代码,则需要编写自己的函数。为此,您需要研究 、 、 等函数。这不是一项简单的任务,但也不是“火箭科学”。以前已经做过很多很多次了......如果你在网上搜索,你很可能会找到一个可以复制的开源实现(确保遵循这样做的规则)。reallocfgetsstrcpymemcpy