我的代码可以吗还是会在某些情况下失败?
Is my code okay or will it fail in some occasion?
我最近了解了键盘缓冲区以及在获得用户输入后清理它的重要性。所以我做了这个函数来处理:
// Get input from user
void get_input(char *buff, int size)
{
fgets(buff, size, stdin);
int newlineIndex = strcspn(buff, "\n");
/* If no newline is found, fgets stopped at `size` and
didn't read all input. Keyboard buffer is dirty */
if (newlineIndex == strlen(buff))
{
// Clear keyboard buffer
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
else
{
// Remove newline from buff
buff[newlineIndex] = '[=10=]';
}
}
此处,buff
是您要存储输入的位置,size
是 sizeof(buff)
。在使用 fgets 读取用户的输入后,我用 strcspn()
查找 fgets 留下的 \n
字符。如果 newlineIndex == strlen(buff)
,未找到 \n
换行符,这意味着用户键入的字符多于 size
,因此我继续使用以下命令清除键盘缓冲区:
int c;
while ((c = getchar()) != '\n' && c != EOF);
否则,键盘缓冲区没有变脏,所以我从用户输入中删除了 \n
,以便以后更轻松地将其作为字符串处理。
我的代码没问题还是我遗漏了什么?有没有更有效的方法呢?
是的,你的代码没问题,应该不会失败。但是总是检查 fgets 是否失败:(来自手册)
If an error occurs, they return NULL and the buffer contents are indeterminate.
它比必要的更复杂和低效,尤其是考虑到 strings work in C 的方式。您不需要搜索 newline。它要么在字符串的末尾,要么不在 - 您唯一需要查看的地方是在字符串的末尾。
fgets()
可以 return NULL
如果例如在读取任何字符之前遇到 EOF - 如果使用来自文件的输入重定向,这很容易发生。所以你应该检查一下。您的代码 将 以不可预测的方式失败 as-is if buff[0] != '[=17=]'
on entry and fgets()
returns NULL
.
它通常对 return 某些东西 有用 - 即使您经常丢弃 return 值。您可以 return 错误指示或 return 有效(但为空)的错误字符串,或者可能 return 输入字符串的长度以保存调用者另一个 strlen()
遍历。我个人建议 returning 指向调用者缓冲区的指针并初始化 buff[0] = '[=21=]'
以确保始终 returned 有效字符串。这样您就可以执行以下操作:
char inp[20] ;
printf( "%s\n", get_input( inp, sizeof(inp) ) ) ;
所以我建议:
char* get_input( char* buff, size_t size )
{
buff[0] = '[=11=]' ;
if( fgets(buff, (int)size, stdin) != NULL )
{
size_t len = strlen( buff ) ;
char* endp = len == 0 ? buff : &buff[len - 1] ;
if( *endp != '\n' )
{
int discard ;
while( (discard = getchar()) != '\n' && discard != EOF ) ;
}
else
{
// Remove newline from buff
*endp = '[=11=]';
}
}
return buff ;
}
一个可能有用的修改,因为您需要确定输入的长度的麻烦是 return 通过参考参数给调用者的长度:
// Get input from user
char* get_input( char *buff, size_t size, size_t* inplen )
{
buff[0] = '[=12=]' ;
size_t len = 0 ;
if( fgets(buff, (int)size, stdin) != NULL )
{
len = strlen( buff ) ;
char* endp = len == 0 ? buff : &buff[len - 1] ;
if( *endp != '\n' )
{
int discard ;
while( (discard = getchar()) != '\n' && discard != EOF ) ;
}
else
{
// Remove newline from buff
*endp = '[=12=]';
len-- ;
}
}
// Return string length via inplen if provided
if( inplen != NULL ) *inplen = len ;
return buff ;
}
然后例如:
char inp[20] ;
size_t length = 0 ;
printf( "%s\n", get_input( inp, sizeof(inp), &length ) ) ;
printf( "%zu characters entered\n", length ) ;
或者如果你想丢弃长度:
get_input( inp, sizeof(inp), NULL ) ;
.... it fail in some occasion?
是的。
缺少对 fgets()
中 return 值的检查。 fgets()
returns NULL
表示立即 end-of-file 或刚刚发生输入错误,get_input()
也应如此。在这种情况下,对于 OP 的代码,buff[]
处于不确定状态,因此 strcspn(buff, "\n");
存在 未定义行为 (UB) 的风险。
代码无法 return 任何成功或错误的指示(end-of-file,此处发生的输入错误或过长的行。)。
极端 缓冲区大小可能超过int
范围。 size_t size
不会超过数组或分配大小。 strcspn(buff, "\n")
return 是 size_t
,而不是 int
。如果值大于 INT_MAX
.
,保存在 int
中将导致问题
如果读取了先前的 空字符 ,strcspn(buff, "\n")
无法检测到 '\n'
。
标准 C 缺乏将 行读入字符串的可靠方法。 fgets()
让我们大部分时间都在那里。
我最近了解了键盘缓冲区以及在获得用户输入后清理它的重要性。所以我做了这个函数来处理:
// Get input from user
void get_input(char *buff, int size)
{
fgets(buff, size, stdin);
int newlineIndex = strcspn(buff, "\n");
/* If no newline is found, fgets stopped at `size` and
didn't read all input. Keyboard buffer is dirty */
if (newlineIndex == strlen(buff))
{
// Clear keyboard buffer
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
else
{
// Remove newline from buff
buff[newlineIndex] = '[=10=]';
}
}
此处,buff
是您要存储输入的位置,size
是 sizeof(buff)
。在使用 fgets 读取用户的输入后,我用 strcspn()
查找 fgets 留下的 \n
字符。如果 newlineIndex == strlen(buff)
,未找到 \n
换行符,这意味着用户键入的字符多于 size
,因此我继续使用以下命令清除键盘缓冲区:
int c;
while ((c = getchar()) != '\n' && c != EOF);
否则,键盘缓冲区没有变脏,所以我从用户输入中删除了 \n
,以便以后更轻松地将其作为字符串处理。
我的代码没问题还是我遗漏了什么?有没有更有效的方法呢?
是的,你的代码没问题,应该不会失败。但是总是检查 fgets 是否失败:(来自手册)
If an error occurs, they return NULL and the buffer contents are indeterminate.
它比必要的更复杂和低效,尤其是考虑到 strings work in C 的方式。您不需要搜索 newline。它要么在字符串的末尾,要么不在 - 您唯一需要查看的地方是在字符串的末尾。
fgets()
可以 return NULL
如果例如在读取任何字符之前遇到 EOF - 如果使用来自文件的输入重定向,这很容易发生。所以你应该检查一下。您的代码 将 以不可预测的方式失败 as-is if buff[0] != '[=17=]'
on entry and fgets()
returns NULL
.
它通常对 return 某些东西 有用 - 即使您经常丢弃 return 值。您可以 return 错误指示或 return 有效(但为空)的错误字符串,或者可能 return 输入字符串的长度以保存调用者另一个 strlen()
遍历。我个人建议 returning 指向调用者缓冲区的指针并初始化 buff[0] = '[=21=]'
以确保始终 returned 有效字符串。这样您就可以执行以下操作:
char inp[20] ;
printf( "%s\n", get_input( inp, sizeof(inp) ) ) ;
所以我建议:
char* get_input( char* buff, size_t size )
{
buff[0] = '[=11=]' ;
if( fgets(buff, (int)size, stdin) != NULL )
{
size_t len = strlen( buff ) ;
char* endp = len == 0 ? buff : &buff[len - 1] ;
if( *endp != '\n' )
{
int discard ;
while( (discard = getchar()) != '\n' && discard != EOF ) ;
}
else
{
// Remove newline from buff
*endp = '[=11=]';
}
}
return buff ;
}
一个可能有用的修改,因为您需要确定输入的长度的麻烦是 return 通过参考参数给调用者的长度:
// Get input from user
char* get_input( char *buff, size_t size, size_t* inplen )
{
buff[0] = '[=12=]' ;
size_t len = 0 ;
if( fgets(buff, (int)size, stdin) != NULL )
{
len = strlen( buff ) ;
char* endp = len == 0 ? buff : &buff[len - 1] ;
if( *endp != '\n' )
{
int discard ;
while( (discard = getchar()) != '\n' && discard != EOF ) ;
}
else
{
// Remove newline from buff
*endp = '[=12=]';
len-- ;
}
}
// Return string length via inplen if provided
if( inplen != NULL ) *inplen = len ;
return buff ;
}
然后例如:
char inp[20] ;
size_t length = 0 ;
printf( "%s\n", get_input( inp, sizeof(inp), &length ) ) ;
printf( "%zu characters entered\n", length ) ;
或者如果你想丢弃长度:
get_input( inp, sizeof(inp), NULL ) ;
.... it fail in some occasion?
是的。
缺少对
fgets()
中 return 值的检查。fgets()
returnsNULL
表示立即 end-of-file 或刚刚发生输入错误,get_input()
也应如此。在这种情况下,对于 OP 的代码,buff[]
处于不确定状态,因此strcspn(buff, "\n");
存在 未定义行为 (UB) 的风险。代码无法 return 任何成功或错误的指示(end-of-file,此处发生的输入错误或过长的行。)。
极端 缓冲区大小可能超过
,保存在int
范围。size_t size
不会超过数组或分配大小。strcspn(buff, "\n")
return 是size_t
,而不是int
。如果值大于INT_MAX
.int
中将导致问题
如果读取了先前的 空字符 ,strcspn(buff, "\n")
无法检测到'\n'
。
标准 C 缺乏将 行读入字符串的可靠方法。 fgets()
让我们大部分时间都在那里。