注意:
Windows Vista では, 「エクスプローラ」のメニューから以下のようにして設定できる.
printf関数を用いて画面に出力するように, ファイルに出力したり, scanf関数を用いてキーボードから入力するように, ファイルに予め値や文字を保存しておき, その内容を変数に入力する方法について述べる.
ファイル処理は, 先頭にfが付いた関数を用い, 以下の手順で行う.
fprintf関数, fscanf関数, fopen関数, fclose関数は stdio.hでプロトタイプ宣言されている. また, FILE構造体も stdio.hの中で定義されている. したがって, fprintf関数やfscanf関数を用いるときは, それに先立って stdio.h ファイルを include しておく必要がある. 以降で, 各関数について説明する.
#include <stdio.h> FILE *fopen(const char *path, const char *mode);path には, 入力または出力を行いたいファイル名を文字列として与える. mode には, 書き込みを行いたいときは"w" (write)を 読み込みを行いたいときは "r" (read)を指定する.
戻り値は, FILE構造体へのポインタである. ファイルを開くことができた場合には, FILE構造体をシステムが用意し, FILE構造体のメンバに必要な値が代入され, それへのポインタのみが返される. FILE構造体は, ファイル処理に必要な情報をメンバとして持つ.
ファイルを開くことに失敗した場合は, NULLを返す. NULLはstdio.hで定義されている定数のアドレスで, エラーが起きたときに返される値として使用される.
#include <stdio.h> int fclose(FILE *fp);fpにより指定されるファイルを閉じる.
以下に 参考のためprintf 関数と fprintf関数のプロトタイプ宣言を比較して挙げる.
#include <stdio.h> int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...);
fprintf関数は, fpで指定されるファイルに, formatで指定された書式に従って, ...で与えられた任意の数の変数の値を出力する. fpとしては, fopen関数で返された FILE構造体のアドレスを用いる.
以下に scanf 関数と fscanf関数のプロトタイプ宣言を挙げる.
#include <stdio.h> int scanf(const char *format, ...); int fscanf(FILE *fp, const char *format, ...);fscanf関数は, fpで指定されるファイルから, formatで指定された書式に従って, ...で与えられた任意の数の変数に入力(代入)を行う.
UNIXを使用している場合は, 端末の中で man (manual) コマンドを用いることにより各関数の詳細な説明を得ることができる. 例えば, printf関数について調べるときには,
$ man printf
#include <stdio.h> int main(void) { FILE *fp; fp = fopen("output.txt", "w"); fprintf(fp, "%d\n", 100); fprintf(fp, "%d\n", 200); return 0; }出力された結果は, output.txtファイルをメモ帳 (Windows), emacs (UNIX), gedit (UNIX)などのエディタで開いて確認できる. またコマンドラインから more (Windows, UNIX), lv (UNIX), cat (UNIX) などのコマンドで内容を画面に表示できる.
$ more output.txt ... $ lv output.txt ... $ cat output.txtlv を終了するときは q を押す. moreやlvで次のページに進むときは スペースキーを押す.
#include <stdio.h> int main(void) { FILE *fp_a; FILE *fp_b; fp_a = fopen("outputA.txt", "w"); fp_b = fopen("outputB.txt", "w"); fprintf(fp_a, "%d\n", 100); fprintf(fp_b, "%d\n", 500); return 0; }
#include <stdio.h> int main(void) { FILE *fp; fp = fopen("output.txt", "w"); if(fp == NULL){ printf("File can not be created."); }else{ fprintf(fp, "%d\n", 100); } return 0; }
#include <stdio.h> int main(void) { FILE *fp_a, *fp_b; fp_a = fopen("outputA.dat", "w"); if(fp_a == NULL){ printf("File A can not be created."); }else{ fprintf(fp_a, "%d\n", 100); fprintf(fp_a, "%d\n", 200); fcolse(fp_a); // 必要がなくなったらできるだけ早くcloseする. } fp_b = fopen("outputB.dat", "w"); if(fp_b == NULL){ printf("File A can not be created."); }else{ fprintf(fp_b, "%d\n", 500); fprintf(fp_b, "%d\n", 600); fcolse(fp_b); // 必要がなくなったらできるだけ早くcloseする. } return 0; }
#include <stdio.h> int main(void) { FILE *fp; // よく使われる表現. if ( (fp = fopen("output.dat", "w")) == NULL ) { printf("File A can not be created."); } else { fprintf(fp, "%d\n", 100); fclose(fp); } return 0; }
[program 1]
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; // よく使われる表現. if ( (fp = fopen("output.dat", "w")) == NULL ) { printf("File A can not be created."); exit(1); } fprintf(fp, "%d\n", 100); fclose(fp); return 0; }
また, exit関数の引数の値や main 関数の戻り値 (main 関数中のreturn 文に与える値)は, プログラムの戻り値としてOSに返される. この値はstdlib.h で定義されていて, プログラムが正常に終了するときEXIT_SUCCESSは返し, エラーで終了するときはEXIT_FAILUREを返すとよい.
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; fp = fopen("output.dat", "w"); if ( fp == NULL ) { printf("File A can not be created."); exit(EXIT_FAILURE); // プログラム異常終了. } else { fprintf(fp, "%d\n", 100); fclose(fp); } return EXIT_SUCCESS; // プログラム正常終了. }
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp_a, *fp_b; fp_a = fopen("outputA.dat", "w"); if(fp == NULL){ printf("File A can not be created."); exit(EXIT_FAILURE); } fp_b = fopen("outputB.dat", "w"); if(fp_b == NULL){ printf("File A can not be created."); exit(EXIT_FAILURE); } fprintf(fp_a, "%d\n", 100); fprintf(fp_b, "%d\n", 500); fclose(fp_a); fclose(fp_b); return EXIT_SUCCESS; }
#include <stdio.h> #include <stdlib.h> FILE *my_fopen(const char *filename, const char *mode) { FILE *fp; fp = fopen(filename, mode); if(fp == NULL){ printf("Fiel %s can not be created.\n", filename); exit(EXIT_FAILURE); } return fp; } int main(void) { FILE *fp; fp = my_fopen("outputA.dat", "w"); fprintf(fp, "%d\n", 100); fclose(fp); fp = my_fopen("outputB.dat", "w"); fprintf(fp, "%d\n", 500); fclose(fp); return EXIT_SUCCESS; }
ここで作成したmy_fopen関数により main関数の内部が簡潔に記述できていることに注意すること. また, ここに示したように, 同時にファイルを開いていないのであれば, 1つのファイルポインタで複数のファイルを操作できる.
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; char *filename = "output.dat"; if((fp = fopen(filename, "w")) == NULL){ perror(filename); exit(EXIT_FAILURE); } fprintf(fp, "%d\n", 100); fclose(fp); return EXIT_SUCCESS; }この例では, ファイルを開くことができない場合
output.dat: No such file or directoryや
output.dat: Permission deniedのように原因に応じてメッセージが表示される.
fscanf関数を用いて, ファイルに書き込んである内容を読み込んでみる.
次の例は, output.datファイルから整数をxに読み込むプログラムである.
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; int x; fp = fopen("output.dat", "r"); if(fp == NULL){ perror("output.dat"); exit(EXIT_FAILURE); } fscanf(fp, "%d", &x); fclose(fp); printf("x =%d\n", x); return EXIT_SUCCESS; }この例では, 読み込みに用いる変数の型(ここではint)およびfscanfの書式(ここでは%d)が, ファイルに書き込んである値の型と一致している必要がある. このプログラムを正しく動作させるためには, 予め別のプログラムで output.txt というファイルを作成し, 1行目に整数を書き込んでおく必要がある. もしくは, emacs, メモ帳などエディタで作成しておいてもよい.
実際にファイルから入力を行う場合には, fscanf関数が読み込みに失敗した場合のエラー処理が必要となる. この処理は煩雑であるので, fgets関数を用いた別法を用いた方がいい.
最後に, output.datファイルに整数を書き込み, 同じプログラム中でそのファイルを開いて値を読み込む例を示す.
#include <stdio.h> #include <stdlib.h> FILE *my_fopen(const char *filename, const char *mode) { FILE *fp; fp = fopen(filename, mode); if(fp == NULL){ perror(filename); exit(EXIT_FAILURE); } return fp; } int main(void) { FILE *fp_w, *fp_r; int x; fp_w = my_fopen("output.dat", "w"); fprintf(fp_w, "%d\n", 100); fclose(fp_w); fp_r = my_fopen("output.dat", "r"); fscanf(fp_r, "%d", &x); fclose(fp_r); printf("x = %d\n", x); return EXIT_SUCCESS; }ここで作成したmy_fopen関数は, ファイルの読み込みにも書き込みにも対応でき, main関数の内部が簡潔に記述できている.
http://www.hcn.zaq.ne.jp/___/REC-SVG11-20030114/index.html
例えば直線の場合, 始点と終点を指定するだけの ベクトル型の画像であるのでどんなに拡大してもギザギザにならない. これに対し, jpeg, bmp, tiff などのラスタ型の画像は点の集まりとして 画像が表現されており, 拡大するとギザギザになってしまう.
SVG ファイルは Firefox などのWebブラウザなどで画像としてで表示することができる. また, Inkscape (Linux, Windows) や Illustrator (Windows) などで編集できる.
SVGファイルは, 例えば下のような内容を持っている.
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <svg xmlns='http://www.w3.org/2000/svg'> <line x1='100' y1='500' x2='500' y2='100' stroke='blue' stroke-width='5'/> </svg>
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <svg xmlns='http://www.w3.org/2000/svg'> <line x1='100' y1='500' x2='500' y2='100' stroke='blue' stroke-width='5' /> </svg>このSVGファイルの構造は次のようになっている.
<line ... />の中の'で囲まれた部分のパラメータ (100, 500, blueなど) を変えて色々な直線を描画してみよう.
<svg>と</svg>で囲まれた部分には, 複数の描画要素を記述できる. 3本の直線を描く場合には次のようになる.
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <svg xmlns='http://www.w3.org/2000/svg'> <line x1='100' y1='500' x2='500' y2='100' stroke='blue' stroke-width='5' /> <line x1='100' y1='600' x2='500' y2='300' stroke='cyan' stroke-width='10' /> <line x1='100' y1='700' x2='500' y2='500' stroke='red' stroke-width='20' /> </svg>
上で挙げた基本図形を描画する例を以下に示す.
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <svg xmlns='http://www.w3.org/2000/svg'> <rect width='750' height='450' x='50' y='150' stroke='black' stroke-width='1' fill='azure' /> <line x1='100' y1='550' x2='700' y2='350' stroke='blue' stroke-width='5' /> <polyline points='100 550, 250 370, 450 520, 700 200' stroke='red' stroke-width='3' fill='none' /> <circle cx='100' cy='550' r='10' fill='yellow' stroke='black' stroke-width='1' /> <text x='100' y='200' font-size='30'>ABC</text> </svg>
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <svg xmlns='http://www.w3.org/2000/svg'> <line x1='100' y1='500' x2='500' y2='100' stroke='blue' stroke-width='5' /> </svg>
#include <stdio.h> int main(void) { printf("<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); printf("<svg xmlns='http://www.w3.org/2000/svg'>\n"); printf("<line x1='100' y1='500' x2='500' y2='100' stroke='blue' stroke-width='5' />\n"); printf("</svg>\n"); return 0; }
[[課題1]]FirefoxなどのWebブラウザによりこのファイルを画像として表示し, 正しく出力されたか確認しよう. 詳細は「SVGファイルを作って表示してみる.」[13.4.2]を参照のこと. 作成したSVGファイルと対応するC言語のソースコードをメールで送ること.
#include <stdio.h> int main(void) { FILE *fp; // (*1) fp = fopen("drawing.svg", "w"); // (*2) if(fp==NULL){ // (*3) printf("drawing.svg: File can not be created."); }else{ fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); // (*4) fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); fprintf(fp, " <line x1='100' y1='500' x2='500' y2='100' stroke='blue' stroke-width='5' />"); fprintf(fp, "</svg>"); fclose(fp); // (*5) } return 0; }
書式の復習:
[[課題2]]以下の変数 x1, y1, x2, y2, stroke, stroke_widthの値を変更することで異なる直線が描かれることを確認しましょう. また, fopenの第1引数として与えている "drawing.svg" の部分を, "drawing2.svg" のように変更すると出力する先のファイルを変更できることを確認しましょう. 作成したSVGファイルと対応するC言語のソースコードをメールで送ること.
#include <stdio.h> int main(void) { FILE *fp; double x1=100.0, y1=500.0, x2=500.0, y2=100.0, stroke_width=5.0; char *stroke = "blue"; fp = fopen("drawing.svg", "w"); if(fp==NULL){ printf("drawing.svg: File can not be created."); }else{ fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); fprintf(fp, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g'/>\n", x1, y1, x2, y2, stroke, stroke_width); fprintf(fp, "</svg>\n"); fclose(fp); } return 0; }
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; double x1=100.0, y1=500.0, x2=500.0, y2=100.0, stroke_width=5.0; char *stroke = "blue"; // --- ファイルを開く. ----------------------------------- fp = fopen("drawing.svg", "w"); if(fp==NULL){ printf("drawing.svg: File can not be created."); exit(1); } // --- ファイル処理 -------------------------------------- fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); fprintf(fp, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g' />\n", x1, y1, x2, y2, stroke, stroke_width); fprintf(fp, "</svg>\n"); // --- ファイルを閉じる. -------------------------------- fclose(fp); // --- 終了. -------------------------------------------- return 0; }
さらに, SVG処理の観点からは, 下に示すように 「描画開始」から「SVG処理終了」の間は何を描画するかで変わるが, その前の「SVG処理開始」から「描画開始」までの準備と, 「SVG処理終了」から「終了」までの後始末はいつも同じである.
#include <stdio.h> #include <stdlib.h> int main(void) { FILE *fp; double x1=100.0, y1=500.0, x2=500.0, y2=100.0, stroke_width=5.0; char *stroke = "blue"; // --- SVG処理開始 --------------------------------------- fp = fopen("drawing.svg", "w"); if(fp==NULL){ printf("drawing.svg: File can not be created."); exit(1); } fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); // --- 描画開始 ------------------------------------------ fprintf(fp, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g' />\n", x1, y1, x2, y2, stroke, stroke_width); // --- SVG処理終了 --------------------------------------- fprintf(fp, "</svg>\n"); fclose(fp); // --- 終了. -------------------------------------------- return 0; }
そこで, 準備の部分をsvg_open関数にし, 後始末の部分をsvg_close関数にする. 今後は, 描画の部分のみを目的に応じて変更すればよいことになる. main関数の中のファイルポインタは, 他の関数の中で遮蔽されて使われているファイルポインタと区別するため, svgという名前に変更した.
#include <stdio.h> #include <stdlib.h> FILE *svg_open(const char *filename) { FILE *fp; fp = fopen(filename, "w"); if(fp==NULL){ printf("drawing.svg: File can not be created."); exit(1); } fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); return fp; } void svg_close(FILE *fp) { fprintf(fp, "</svg>\n"); fclose(fp); } int main(void) { FILE *svg; double x1=100.0, y1=500.0, x2=500.0, y2=100.0, stroke_width=5.0; char *stroke = "blue"; // --- SVG処理開始 --------------------------------------- svg = svg_open("drawing.svg"); // --- 描画開始 ------------------------------------------ fprintf(svg, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g' />\n", x1, y1, x2, y2, stroke, stroke_width); // --- SVG処理終了 --------------------------------------- svg_close(svg); // --- 終了. -------------------------------------------- return 0; }
さらに, 基本図形の描画も関数化しておくと, 中心となるmain関数の中では毎回煩雑な記述をしなくて済むようになる. 次の例では, <line ... /> を出力する部分を line_draw 関数にした.
#include <stdio.h> #include <stdlib.h> FILE *svg_open(const char *filename) { FILE *fp; fp = fopen(filename, "w"); if(fp==NULL){ printf("drawing.svg: File can not be created."); exit(1); } fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); return fp; } void svg_close(FILE *fp) { fprintf(fp, "</svg>\n"); fclose(fp); } void line_draw(FILE *fp, double x1, double y1, double x2, double y2, const char *stroke, double stroke_width) { fprintf(fp, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g' />\n", x1, y1, x2, y2, stroke, stroke_width); } // 異なる描画を行うためには, ここから下の部分を書き換えるだけでよい. int main(void) { FILE *svg; double x1=100.0, y1=500.0, x2=500.0, y2=100.0, stroke_width=5.0; char *stroke = "blue"; svg = svg_open("drawing.svg"); line_draw(svg, x1, y1, x2, y2, stroke, stroke_width); svg_close(svg); return 0; }
ここでは stroke と stroke_width をメンバに持つ構造体 ATTRIBUTE を定義する.
#include <stdio.h> #include <stdlib.h> typedef struct{ char *stroke; double stroke_width; } ATTRIBUTE; FILE *svg_open(const char *filename) { FILE *fp; fp = fopen(filename, "w"); if(fp==NULL){ printf("drawing.svg: File can not be created.\n"); exit(1); } fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); return fp; } void svg_close(FILE *fp) { fprintf(fp, "</svg>\n"); fclose(fp); } void line_draw(FILE *fp, double x1, double y1, double x2, double y2, const ATTRIBUTE *attrib) { fprintf(fp, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g'/>\n", x1, y1, x2, y2, attrib->stroke, attrib->stroke_width); } // 異なる描画を行うためには, ここから下の部分を書き換えるだけでよい. int main(void) { FILE *svg; ATTRIBUTE attrib = {"blue", 5.0}; svg = svg_open("drawing.svg"); line_draw(svg, 100.0, 500.0, 500.0, 100.0, &attrib); svg_close(svg); return 0; }
[[課題3]] 以下のように直線 <line .../> を自由に描画できるようになった. main関数の中を書き換えて, 例とは異なる直線を描いてみましょう. 出力したSVGファイルと対応するC言語のソースコードをメールで送ること.
#include <stdio.h> #include <stdlib.h> typedef struct{ char *stroke; double stroke_width; } ATTRIBUTE; FILE *svg_open(const char *filename) { FILE *fp; fp = fopen(filename, "w"); if(fp==NULL){ printf("drawing.svg: File can not be created."); exit(1); } fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); return fp; } void svg_close(FILE *fp) { fprintf(fp, "</svg>\n"); fclose(fp); } void line_draw(FILE *fp, double x1, double y1, double x2, double y2, const ATTRIBUTE *attrib) { fprintf(fp, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g' />\n", x1, y1, x2, y2, attrib->stroke, attrib->stroke_width); } // 異なる描画を行うためには, ここから下の部分を書き換えるだけでよい. int main(void) { FILE *svg; ATTRIBUTE normal = {"blue", 2.0}, alert = {"red", 5.0}; svg = svg_open("drawing.svg"); line_draw(svg, 100.0, 500.0, 500.0, 100.0, &normal); line_draw(svg, 100.0, 600.0, 500.0, 200.0, &normal); line_draw(svg, 100.0, 100.0, 500.0, 500.0, &alert); svg_close(svg); return 0; }
他の基本図形も関数により描画できるようにした. このために, ATTRIBUTE構造体に fill と font_size をメンバとして加えた. このような変更を行っても先に作成した line_draw 関数に変更を加える必要がないことに注意すること. ただし, mainの中で行っている構造体の初期化の部分は変更する必要がある.
(C99 では, ATTRIBUTE attrib = .stroke="blue", .stroke_width=5のようにメンバ名を指定して初期化ができるので, 構造体のメンバが増えたり, メンバの並び順が変わっても, このような変更も生じない.)
以下では, <rect ... />, <line ... />, <polyline ... />, <circle ... />, <text>...</text>を 関数を用いて描画できるようになった.
[[課題4]] main 関数を変更し下に載せる例とは異なる図形を描画しましょう. 出力したSVGファイルと対応するC言語のソースコードをメールで送ること.
#include <stdio.h> #include <stdlib.h> typedef struct{ char *stroke; double stroke_width; char *fill; double font_size; } ATTRIBUTE; FILE *svg_open(const char *filename) { FILE *fp; fp = fopen(filename, "w"); if(fp==NULL){ printf("drawing.svg: File can not be created."); exit(1); } fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); return fp; } void svg_close(FILE *fp) { fprintf(fp, "</svg>\n"); fclose(fp); } void line_draw(FILE *fp, double x1, double y1, double x2, double y2, const ATTRIBUTE *attrib) { fprintf(fp, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g'/>\n", x1, y1, x2, y2, attrib->stroke, attrib->stroke_width); } void rect_draw(FILE *fp, double width, double height, double x, double y, const ATTRIBUTE *attrib) { fprintf(fp, "<rect width='%g' height='%g' x='%g' y='%g' stroke='%s' stroke-width='%g' fill='%s' />\n", width, height, x, y, attrib->stroke, attrib->stroke_width, attrib->fill); } void polyline_draw(FILE *fp, const double *x, const double *y, size_t n, const ATTRIBUTE *attrib) { size_t i; fprintf(fp, "<polyline points='"); for(i=0; i<n-1; i++) fprintf(fp, "%g %g, ", x[i], y[i]); fprintf(fp, "%g %g'", x[n-1], y[n-1]); fprintf(fp, " stroke='%s' stroke-width='%g' fill='%s' />\n", attrib->stroke, attrib->stroke_width, attrib->fill); } void circle_draw(FILE *fp, double cx, double cy, double r, const ATTRIBUTE *attrib) { fprintf(fp, "<circle cx='%g' cy='%g' r='%g' stroke='%s' stroke-width='%g' fill='%s' />\n", cx, cy, r, attrib->stroke, attrib->stroke_width, attrib->fill); } void text_draw(FILE *fp, double x, double y, const char *text, const ATTRIBUTE *attrib) { fprintf(fp, "<text x='%g' y='%g' font-size='%g'>%s</text>\n", x, y, attrib->font_size, text); } // 異なる描画を行うためには, ここから下の部分を書き換えるだけでよい. int main(void) { FILE *svg; ATTRIBUTE rect_attrib = {"black", 1.0, "azure", 0.0}; ATTRIBUTE line_attrib = {"blue", 5.0, "none", 0.0}; ATTRIBUTE polyline_attrib = {"red", 3.0, "none", 0.0}; ATTRIBUTE circle_attrib = {"black", 1.0, "yellow", 0.0}; ATTRIBUTE text_attrib = {"black", 0.0, "green", 30.0}; double x[4] = {100.0, 250.0, 450.0, 700.0}; double y[4] = {550.0, 370.0, 520.0, 200.0}; svg = svg_open("drawing.svg"); rect_draw(svg, 750.0, 450.0, 50.0, 150.0, &rect_attrib); line_draw(svg, 100, 550, 700, 350, &line_attrib); polyline_draw(svg, x, y, 4, &polyline_attrib); circle_draw(svg, 100, 550, 10, &circle_attrib); text_draw(svg, 100, 200, "ABC", &text_attrib); svg_close(svg); return 0; }
描画に必要な事柄をまとめておく.
typedef struct{ char *stroke; // 線の色 double stroke_width; // 線の太さ char *fill; // 内部を塗りつぶす色 double font_size; // 文字の大きさ } ATTRIBUTE;
例えば 4つの点 (1,2) (11, 12) (21, 22) (31, 32)をこの順に結んでできる折れ線は,
x[4] = {1, 11, 21, 31}, y[4] = {2, 12, 22, 32}, n=4 と表現できる.
この様な場合に, 描画を行う関数などのどのプログラムでも共通の部分と, 各プログラムに固有な部分を別けると効率がよい. 本来は別のソースファイルとして分割しそれぞれをコンパイルした後結合するという手順を踏む.
ここでは, 簡便のため共通部分をヘッダファイルにしてみる. そのために, 以下のように固有部分と共通部分に分割する.
固有な部分は次のように簡潔に記述できる. 新しいプログラムを作成するときは, このファイルのみを書き直せばよい.
#include <stdio.h> #include "svg0.h" int main(void) { FILE *svg; ATTRIBUTE rect_attrib = {"black", 1.0, "azure", 0.0}; ATTRIBUTE line_attrib = {"blue", 5.0, "none", 0.0}; ATTRIBUTE polyline_attrib = {"red", 3.0, "none", 0.0}; ATTRIBUTE circle_attrib = {"black", 1.0, "yellow", 0.0}; ATTRIBUTE text_attrib = {"black", 0.0, "green", 30.0}; double x[4] = {100.0, 250.0, 450.0, 700.0}; double y[4] = {550.0, 370.0, 520.0, 200.0}; svg = svg_open("drawing.svg"); rect_draw(svg, 750.0, 450.0, 50.0, 150.0, &rect_attrib); line_draw(svg, 100, 550, 700, 350, &line_attrib); polyline_draw(svg, x, y, 4, &polyline_attrib); circle_draw(svg, 100, 550, 10, &circle_attrib); text_draw(svg, 100, 200, "ABC", &text_attrib); svg_close(svg); return 0; }
共通部分は svg0.h というファイル名で, main関数があるファイルと同じフォルダ/ディレクトリに保存しておく. これを #include 命令によりファイルの内容をその行に展開する. この場合は, "svg0.h" のように 二重引用符"でファイル名を囲み, 標準で用意されているヘッダーファイルとは異なることを明示する.
svg0.hの内容は以下のようになる. 今後はこのファイルは変更しないで, 複数のプログラムで共通に使用することができる.
#include <stdio.h> #include <stdlib.h> typedef struct{ char *stroke; double stroke_width; char *fill; double font_size; } ATTRIBUTE; FILE *svg_open(const char *filename) { FILE *fp; fp = fopen(filename, "w"); if(fp==NULL){ printf("drawing.svg: File can not be created."); exit(1); } fprintf(fp, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"); fprintf(fp, "<svg xmlns='http://www.w3.org/2000/svg'>\n"); return fp; } void svg_close(FILE *fp) { fprintf(fp, "</svg>\n"); fclose(fp); } void line_draw(FILE *fp, double x1, double y1, double x2, double y2, const ATTRIBUTE *attrib) { fprintf(fp, "<line x1='%g' y1='%g' x2='%g' y2='%g' stroke='%s' stroke-width='%g'/>\n", x1, y1, x2, y2, attrib->stroke, attrib->stroke_width); } void rect_draw(FILE *fp, double width, double height, double x, double y, const ATTRIBUTE *attrib) { fprintf(fp, "<rect width='%g' height='%g' x='%g' y='%g' stroke='%s' stroke-width='%g' fill='%s' />\n", width, height, x, y, attrib->stroke, attrib->stroke_width, attrib->fill); } void polyline_draw(FILE *fp, const double *x, const double *y, size_t n, const ATTRIBUTE *attrib) { size_t i; fprintf(fp, "<polyline points='"); for(i=0; i<n-1; i++) fprintf(fp, "%g %g, ", x[i], y[i]); fprintf(fp, "%g %g'", x[n-1], y[n-1]); fprintf(fp, " stroke='%s' stroke-width='%g' fill='%s' />\n", attrib->stroke, attrib->stroke_width, attrib->fill); } void circle_draw(FILE *fp, double cx, double cy, double r, const ATTRIBUTE *attrib) { fprintf(fp, "<circle cx='%g' cy='%g' r='%g' stroke='%s' stroke-width='%g' fill='%s' />\n", cx, cy, r, attrib->stroke, attrib->stroke_width, attrib->fill); } void text_draw(FILE *fp, double x, double y, const char *text, const ATTRIBUTE *attrib) { fprintf(fp, "<text x='%g' y='%g' font-size='%g'>%s</text>\n", x, y, attrib->font_size, text); }
(c)1999-2013 Tetsuya Makimura