C で文字列を構築するために、FILE* に出力する出力ルーチンを使用できますか?
-
13-09-2019 - |
質問
この質問に対する答えが「ノー」であるとは悪い予感がしますが、誰かが賢いアイデアを思いついた場合に備えて、これを公開しておきたいと思います。
複雑なデータ構造を取得し、テキスト形式で出力する一連の出力ルーチンがあります。次のようなプロトタイプがあります。
void print_mystruct(struct mystruct *s, FILE *stream)
このように書いたのは、ターミナル、ファイル、ネットワークなどに効率的にバッファリングされた出力を取得できるためです。
残念ながら、標準の C99 を使用して、これらと同じルーチンを使用してメモリ内に文字列を構築する方法は私が知る限りありません。
そこで私の質問は次のとおりです。
- fputs()、fprintf() などを効率的に使用できる賢い方法はありますか?文字列に出力するには?
- そうでない場合、バッファリングされたファイル出力と文字列構築の両方を効率的に実行できる、より良いパラダイムはあるでしょうか?私が考える最善の方法は、(FILE* の代わりに) vtable を使用した独自の構造を持つことです。vtable には、文字列に追加するか fwrite を呼び出す出力関数があります。ただし、その場合は printf ラッパーも作成する必要があります。
他に何か賢いアイデアはありますか?
編集:私はそれを発見しました fmemopen()
POSIX.1-2008 の一部です (以下を参照) fmemopen()) しかし、少なくとも GNU libc マンページによれば、広くサポートされていません。
解決
これを行うことのないポータブルな方法はありません。 glibcのシステム(Linux)が open_memstream / fmemopen に、他のシステムではないかもしれない持っていますそれのようなものを持っています。
ポータブルな方法は、ファイルへの書き込みとバック文字列にそれを読むことです。 、または懸念を分離します。代わりに実装するための
void print_mystruct(struct mystruct *s,FILE *f);
あなたは、例えばだろう実装
char *mystruct_2_str(struct mystruct *s);
どの動的に文字列を割り当てます(または、バッファに渡す)、標準文字列関数(のsnprintfなど)を持つ文字列にフォーマットして、呼び出し元がFILE *にそれを記述するかどうかを決定する必要があります。
他のヒント
fmemopen
と同じ効果を得るためにポータブル(以前のすべてのPOSIXターゲットへの)方法ですが、それはかなり高価です。。パイプと新しい切り離されたスレッドを作成し、呼び出し元のスレッドにパイプの書き終わりをfdopen
。新しいスレッドは、パイプのリーディング端部から読み出して、文字列バッファにデータを入れます。それはパイプからEOFを取得するときに、新しいスレッドのリターンを持っています。それが切り離さだから、行うには、クリーンアップはありません。
確かに、FILE構造体についての魔法のようなものは、ありません。ここで定義の少なくとも一つのバージョンがあります。
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
あなたは、文字列バッファから独自にこれらの構造体の1を作製し、あなたの関数に渡し、また、メモリを解放しティアダウン機能を作成することができます。
あなたのバージョンで使用できるCRTライブラリの呼び出し質問は、ありますか? 1が存在しないので、明らかにファイル名を参照するものは、失敗します。しかし、あなたはおそらく、ポインタを操作し、必要に応じてより多くのスペースを割り当てるされる、関数fwriteおよび関数freadとfseekのような関数を使用することができます。
#include <stdio.h>
#include <stdlib.h>
char* file_to_string(FILE *f, int *len) {
if (fseek(f, 0, SEEK_END)) handle_error();
int buflen = ftell(f);
if (len) *len = buflen;
char *buf = malloc(buflen + 1);
buf[buflen] = '\0';
rewind(f);
size_t readlen = fread(buf, 1, buflen, f);
if (readlen != buflen) handle_error();
// in particular, note readlen might not equal buflen in the face of text-mode
// conversions, but tmpfile() indicates binary-mode, so doesn't affect this
// case
return buf;
}
int main() {
FILE *temp = tmpfile();
// use FILE interface
fputs("written to temporary file\n", temp);
char *s = file_to_string(temp, NULL);
// now you have contents as a string
free(s);
// can also rewind the FILE and process it incrementally, etc.
return 0;
}