假设您要打开一个文件,然后将文件句柄传递给您的函数。您在函数中的代码仍然必须读取该常规文件上的 EOF。此外,它必须处理分配足够的空间来存储文件,并处理短读取。
所有这些都是您必须为标准输入处理的同一组问题 - 唯一可能的区别是来自终端的标准输入将为您提供每行输入的短读,而从管道中读取的每次读都会给您一个短读取管道缓冲区的大小(或小于缓冲区大小的原子写入),而普通磁盘文件通常只会在文件的最后一个块上为您提供短暂的读取。由于您的函数无法提前告知需要多少空间(当然不是用于管道或终端输入),因此您必须准备好处理动态内存分配 -malloc()
和realloc()
.
此外,如果您的函数期望获取已经为它读取的数据,为什么它被传递一个文件句柄(FILE 指针)而不是字符缓冲区及其长度?当您需要函数使用它时,您将文件句柄传递给函数 - 从可读句柄读取,或写入可写句柄(并且,偶尔,如果句柄打开以进行读取和写入)。
这是一个工作示例程序。我必须想办法把整个文件塞进内存,处理它,然后给出一些答案——所以我选择了按字符对文件进行排序。适度无意义,但它演示了该做什么。它还有一个操作变量参数错误报告功能。
玩得开心!
/*
* Demo code for StackOverflow question 1484693
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
static char *arg0;
static void error(const char *fmt, ...)
{
va_list args;
int errnum = errno; /* Catch errno before it changes */
fprintf(stderr, "%s: ", arg0);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
fputc('\n', stderr);
exit(1);
}
static int char_compare(const void *v1, const void *v2)
{
char c1 = *(const char *)v1;
char c2 = *(const char *)v2;
if (c1 < c2)
return -1;
else if (c1 > c2)
return +1;
else
return 0;
}
static void process_my_file(FILE *fp)
{
char *buffer;
size_t buflen = 1024;
size_t in_use = 0;
ssize_t nbytes;
if ((buffer = malloc(buflen)) == 0)
error("out of memory - malloc()");
while ((nbytes = fread(buffer + in_use, sizeof(char), buflen - in_use, fp)) > 0)
{
if (nbytes < 0)
error("error from fread()");
in_use += nbytes;
if (in_use >= buflen)
{
char *newbuf;
buflen += 1024;
if ((newbuf = realloc(buffer, buflen)) == 0)
error("out of memory - realloc()");
buffer = newbuf;
}
}
/* Consistency - number/size vs size/number! */
qsort(buffer, in_use, sizeof(char), char_compare);
fwrite(buffer, sizeof(char), in_use, stdout);
putchar('\n');
free(buffer);
}
int main(int argc, char **argv)
{
arg0 = argv[0];
if (argc > 1)
{
for (int i = 1; i < argc; i++)
{
FILE *fp;
if ((fp = fopen(argv[i], "r")) == 0)
error("failed to open file %s", argv[i]);
process_my_file(fp);
fclose(fp);
}
}
else
process_my_file(stdin);
return(0);
}
您可以使用一个或多个文件名作为参数来调用它;每个文件名都单独排序。您可以将某些东西插入其中;你可以让它从标准输入中读取。fwrite()
我选择忽略并fclose()
可能失败的可能性;我也选择忽略 in 上溢出的buflen
可能性process_my_file()
。如果您愿意,可以检查它们。(请注意,每个文件的输出都比输入多一个换行符。)
读者练习:
- 将不可打印的字符打印为 ''\xXX`' 转义序列。
- 将输出分成每行不超过 64 个字符的行。
- 设计或研究替代分配策略,例如将每次分配的空间加倍(参见“编程实践”)