我正在尝试编写一个执行以下操作的函数:
'> '
。'\0'
。 '\n'
这就是我目前所写的:
void input_loop(){
char *str = NULL;
printf("> ");
while(printf("> ") && scanf("%a[^\n]%*c",&input) == 1){
/*Add null byte to the end of str*/
/*Do stuff to input, including traversing until the null byte is reached*/
free(str);
str = NULL;
}
free(str);
str = NULL;
}
现在,我不太确定如何将空字节添加到字符串的末尾。我在想这样的事情:
last_index = strlen(str);
str[last_index] = '\0';
但我不太确定这是否会奏效。我无法测试它是否可行,因为我在尝试编译代码时遇到此错误:
warning: ISO C does not support the 'a' scanf flag [-Wformat=]
那么我该怎么做才能使我的代码有效呢?
编辑:将scanf("%a[^\n]%*c",&input) == 1
更改为scanf("%as[^\n]%*c",&input) == 1
会给我带来同样的错误。
答案 0 :(得分:2)
首先,scanf格式字符串不使用正则表达式,所以我不认为接近你想要的东西会起作用。至于您得到的错误according to my trusty manual,%a
转换标志用于浮点数,但它仅适用于C99(并且您的编译器可能配置为C90)
但是你有一个更大的问题。 scanf期望您传递一个先前分配的空缓冲区,以便用读取输入填充它。它不会为你的sctring malloc,所以你尝试将str初始化为NULL和相应的frees将无法用于scanf。
你能做的最简单的事就是放弃n个行长字符串。创建一个大缓冲区并禁止长于此的输入。
然后,您可以使用fgets功能填充缓冲区。要检查它是否设法读取整行,请检查您的字符串是否以" \ n"结束。
char str[256+1];
while(true){
printf("> ");
if(!fgets(str, sizeof str, stdin)){
//error or end of file
break;
}
size_t len = strlen(str);
if(len + 1 == sizeof str){
//user typed something too long
exit(1);
}
printf("user typed %s", str);
}
另一种选择是您可以使用非标准库函数。例如,在Linux中有getline函数,它在后台使用malloc读取整行输入。
答案 1 :(得分:1)
没有错误检查,在您完成操作时不要忘记释放指针。如果你使用这段代码阅读巨大的线条,你应该得到它带来的所有痛苦。
#include <stdio.h>
#include <stdlib.h>
char *readInfiniteString() {
int l = 256;
char *buf = malloc(l);
int p = 0;
char ch;
ch = getchar();
while(ch != '\n') {
buf[p++] = ch;
if (p == l) {
l += 256;
buf = realloc(buf, l);
}
ch = getchar();
}
buf[p] = '\0';
return buf;
}
int main(int argc, char *argv[]) {
printf("> ");
char *buf = readInfiniteString();
printf("%s\n", buf);
free(buf);
}
答案 2 :(得分:1)
如果您使用的是POSIX系统(如Linux),则应该可以访问getline
。可以使其行为类似于fgets
,但如果以空指针和零长度开头,它将为您处理内存分配。
你可以像这样使用in:
#include <stdlib.h>
#include <stdio.h>
#include <string.h> // for strcmp
int main(void)
{
char *line = NULL;
size_t nline = 0;
for (;;) {
ptrdiff_t n;
printf("> ");
// read line, allocating as necessary
n = getline(&line, &nline, stdin);
if (n < 0) break;
// remove trailing newline
if (n && line[n - 1] == '\n') line[n - 1] = '\0';
// do stuff
printf("'%s'\n", line);
if (strcmp("quit", line) == 0) break;
}
free(line);
printf("\nBye\n");
return 0;
}
传递的指针和长度值必须一致,以便getline
可以根据需要重新分配内存。 (这意味着你不应该在循环中更改nline
或指针line
。)如果该行适合,则每次循环使用相同的缓冲区,这样你就有了当你完成阅读时,只需要free
一行字符串。
答案 3 :(得分:1)
有些人提到scanf
可能不适用于此目的。我也不建议使用fgets
。虽然它稍微适合一些,但至少在开始时有些问题似乎难以避免。很少有C程序员第一次在没有完全阅读the fgets
manual的情况下设法正确使用fgets
。大多数人完全忽视的部分是:
EOF
或遇到错误时会发生什么。
fgets()
函数应将stream
中的字节读取到s
指向的数组中,直到读取n-1
个字节,或者读取a并将其传送到{{ 1}},或遇到文件结束条件。然后该字符串以空字节终止。成功完成后,
s
将返回fgets()
。如果流位于文件结尾,则应设置流的文件结束指示符,s
将返回空指针。如果发生读错误,则应设置流的错误指示符,fgets()
将返回空指针...
我觉得我不需要强调太多检查返回值的重要性,所以我再也不提了。可以这么说,如果你的程序没有检查你的程序在fgets()
或错误发生时不知道的返回值;你的程序可能会陷入无限循环。
当没有EOF
时,该行的剩余字节尚未被读取。因此,'\n'
将始终在内部至少解析一次该行。当您引入额外的逻辑时,为了检查fgets
,您将再次解析数据。
这允许您'\n'
存储并再次调用realloc
,如果您想动态调整存储大小,或丢弃该行的其余部分(警告用户截断是个好主意) ,也许使用像fgets
这样的东西。
hugomg提到在动态调整大小代码中使用乘法来避免二次运行时问题。沿着这条线,避免在每次迭代中反复解析相同的数据(因此引入进一步的二次运行时问题)是一个好主意。这可以通过存储您在某处读取(和解析)的字节数来实现。例如:
fscanf(file, "%*[^\n]");
那些设法阅读本手册并提出正确的内容(如此)的人很快就会发现char *get_dynamic_line(FILE *f) {
size_t bytes_read = 0;
char *bytes = NULL, *temp;
do {
size_t alloc_size = bytes_read * 2 + 1;
temp = realloc(bytes, alloc_size);
if (temp == NULL) {
free(bytes);
return NULL;
}
bytes = temp;
temp = fgets(bytes + bytes_read, alloc_size - bytes_read, f); /* Parsing data the first time */
bytes_read += strcspn(bytes + bytes_read, "\n"); /* Parsing data the second time */
} while (temp && bytes[bytes_read] != '\n');
bytes[bytes_read] = '\0';
return bytes;
}
解决方案的复杂性至少是使用fgets
的同一解决方案的两倍。 。我们可以避免使用fgetc
第二次解析数据,因此使用fgetc
似乎是最合适的。大多数C程序员在忽略the fgetc
manual时也设法错误地使用fgetc
。
最重要的细节是要意识到fgetc
会返回fgetc
,而不是int
。 可能通常返回256个不同值中的一个,介于char
和0
之间(包括)。它可以返回UCHAR_MAX
,意味着通常有257个不同的值,EOF
(或因此,fgetc
)可能会返回。尝试将这些值存储到getchar
或char
会导致信息丢失,尤其是错误模式。 (当然,如果unsigned char
大于8,则此典型值257会发生变化,因此CHAR_BIT
大于255)
UCHAR_MAX