ファイルを開き、ファイル ハンドルを関数に渡すとします。関数内のコードは、その通常のファイルで EOF まで読み取る必要があります。さらに、ファイルを保存するのに十分なスペースを割り当て、短い読み取りを処理する必要があります。
これはすべて、stdin で対処しなければならない問題のセットとまったく同じです。唯一の違いは、端末からの stdin では入力の各行に対して短い読み取りが行われるのに対して、パイプからの各読み取りでは短い読み取りが行われることです。パイプ バッファのサイズを読み取る (またはバッファ サイズより小さいアトミック書き込み)。通常、プレーン ディスク ファイルでは、ファイルの最後のブロックで短い読み取りしかできません。関数はどのくらいのスペースが必要かを前もって知ることができないため (確かに、パイプまたは端末の入力用ではありません)、動的メモリ割り当てを処理する準備ができている必要が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);
}
1 つ以上のファイル名を引数としてこれを呼び出すことができます。各ファイル名は個別にソートされます。何かをパイプすることができます。標準入力から読み取らせることができます。私は失敗する可能性fwrite()
を無視することにしました。fclose()
でのオーバーフローの可能性も無視することにしbuflen
ましたprocess_my_file()
。必要に応じてチェックできます。(各ファイルの出力には、入力よりも改行が 1 つ多いことに注意してください。)
読者のための演習:
- 印刷できない文字を ''\xXX`' エスケープ シーケンスとして出力します。
- 出力をそれぞれ 64 文字以下の行に分割します。
- 各割り当てのスペースを 2 倍にするなど、別の割り当て戦略を考案または研究します (「プログラミングの実践」を参照) 。