用户自制的 fgets() 与标准库 fgets()
User-made fgets() vs standard library fgets()
标准库函数fgets()
有两个缺点:
- 函数的第二个参数类型为
int
- 它在提供的缓冲区中留下一个尾随换行符
我做了一个类似于 fgets()
的简单功能,排除了上述缺点,试图提高我的一个程序的效率,该程序获取文本文件的行并终止 char
数组位于换行符处,使用函数 strcspn()
.
但是真的更高效吗?为什么标准库函数比以下简单的实现有优势?
#include <stdio.h>
char *my_fgets(char *buf, size_t maxCount, FILE *stream);
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s [filename]", argv[0]);
}
FILE *fp;
fp = fopen(argv[1], "r");
if (!fp)
{
perror(argv[1]);
return 1;
}
char buf[256];
/*while (fgets(buf, sizeof(buf), fp))
{
buf[strcspn(buf, "\n")] = '[=10=]';
// . . .
puts(buf);
}*/
while (my_fgets(buf, sizeof(buf) - 1, fp))
{
puts(buf);
}
return 0;
}
char *my_fgets(char *buf,
size_t maxCount, FILE *stream)
{
int ch;
size_t n = 0;
while ((ch = fgetc(stream)) != EOF)
{
if (ch == '\n' || n == maxCount)
{
break;
}
else
{
buf[n++] = ch;
}
}
if (n == 0 && ch == EOF)
{
return NULL;
}
buf[n] = '[=10=]';
return buf;
}
is it really more efficient?
否 - 性能不更多。 my_fgets()
充其量也可能具有类似的效率,但即使这样也不太可能,因为 fgets()
可以访问精心制作的汇编代码,而 my_fgets()
不能 .
是的,如果功能目标是获取一行用户输入,它可能会提高编码效率 not save the '\n'
,因为这比 fgets() ... code_to_rid_\n()
更容易编码
然而效率是次要的功能。
my_fgets()
在 上有小问题。
my_fgets()
可以删除字符,不像 fgets()
size_t maxCount
的作用与fgets()
的类似论证相差1个。
很好的尝试,因为标准库 fgets()
有各种弱点,迫使编码人员尝试更好的低级输入线功能。
性能
您使用 fgetc()
一个一个地获取字符保证 my_fgets()
无法与合理优化的解决方案竞争。为了获得良好的性能,您需要通过 read()
系统调用将一些字符读入缓冲区,然后直接使用该缓冲区中的数据。为每个字节执行完整的函数调用是 很多 的开销。
安全
fgets()
对于一般用法 不安全 :它会拆分长行。这种情况通常是无法预料的(例如,您的示例代码也不处理这种情况),并且当向您的程序提供非常长的输入行时,通常会导致错误行为。尾随 \n
的存在实际上可以帮助您解决该问题。 如果你想要安全的软件,你必须在使用fgets()
时明确处理缓冲区不足的情况。如果不这样做,你肯定有一个等待攻击的错误。您的实施甚至没有尝试解决这个问题。
这就是为什么我强烈建议使用可以处理任何长度的输入行的输入函数。在 POSIX-compliant 系统上会是 getline()
:此函数为您分配足够大的缓冲区,因此您的唯一限制是可用 RAM。如果 getline()
在您的平台上不可用,我建议重新实现其功能,而不是 fgets()
.
的 "somewhat better" 版本
标准库函数fgets()
有两个缺点:
- 函数的第二个参数类型为
int
- 它在提供的缓冲区中留下一个尾随换行符
我做了一个类似于 fgets()
的简单功能,排除了上述缺点,试图提高我的一个程序的效率,该程序获取文本文件的行并终止 char
数组位于换行符处,使用函数 strcspn()
.
但是真的更高效吗?为什么标准库函数比以下简单的实现有优势?
#include <stdio.h>
char *my_fgets(char *buf, size_t maxCount, FILE *stream);
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s [filename]", argv[0]);
}
FILE *fp;
fp = fopen(argv[1], "r");
if (!fp)
{
perror(argv[1]);
return 1;
}
char buf[256];
/*while (fgets(buf, sizeof(buf), fp))
{
buf[strcspn(buf, "\n")] = '[=10=]';
// . . .
puts(buf);
}*/
while (my_fgets(buf, sizeof(buf) - 1, fp))
{
puts(buf);
}
return 0;
}
char *my_fgets(char *buf,
size_t maxCount, FILE *stream)
{
int ch;
size_t n = 0;
while ((ch = fgetc(stream)) != EOF)
{
if (ch == '\n' || n == maxCount)
{
break;
}
else
{
buf[n++] = ch;
}
}
if (n == 0 && ch == EOF)
{
return NULL;
}
buf[n] = '[=10=]';
return buf;
}
is it really more efficient?
否 - 性能不更多。 my_fgets()
充其量也可能具有类似的效率,但即使这样也不太可能,因为 fgets()
可以访问精心制作的汇编代码,而 my_fgets()
不能
是的,如果功能目标是获取一行用户输入,它可能会提高编码效率 not save the '\n'
,因为这比 fgets() ... code_to_rid_\n()
然而效率是次要的功能。
my_fgets()
在
my_fgets()
可以删除字符,不像 fgets()
size_t maxCount
的作用与fgets()
的类似论证相差1个。
很好的尝试,因为标准库 fgets()
有各种弱点,迫使编码人员尝试更好的低级输入线功能。
性能
您使用 fgetc()
一个一个地获取字符保证 my_fgets()
无法与合理优化的解决方案竞争。为了获得良好的性能,您需要通过 read()
系统调用将一些字符读入缓冲区,然后直接使用该缓冲区中的数据。为每个字节执行完整的函数调用是 很多 的开销。
安全
fgets()
对于一般用法 不安全 :它会拆分长行。这种情况通常是无法预料的(例如,您的示例代码也不处理这种情况),并且当向您的程序提供非常长的输入行时,通常会导致错误行为。尾随 \n
的存在实际上可以帮助您解决该问题。 如果你想要安全的软件,你必须在使用fgets()
时明确处理缓冲区不足的情况。如果不这样做,你肯定有一个等待攻击的错误。您的实施甚至没有尝试解决这个问题。
这就是为什么我强烈建议使用可以处理任何长度的输入行的输入函数。在 POSIX-compliant 系统上会是 getline()
:此函数为您分配足够大的缓冲区,因此您的唯一限制是可用 RAM。如果 getline()
在您的平台上不可用,我建议重新实现其功能,而不是 fgets()
.