''[[FrontPage]]''
* ファイルの入出力2 [#ya50b151]
UNIX上でテキストファイルのコピーを行う実験プログラム。
** 制約 [#g3826ebb]
コピーするのに1行単位、または1文字単位で読書きするような方法をとらず、必ず1処理で終わらせること。~
移植性を高めるためにシステムコールは使用禁止とする。~
~
システムコール以外のファイルサイズ取得にstat()があるが、stat関数はWindows環境でも機能する。(statは使用可)~
~
さらによい方法が見つかれば更新します。
#include <stdio.h> /* */
#include <sys/stat.h> /* for stat() */
/*#include <sys/types.h> for stat() */
/*#include <unistd.h> for stat() * 環境によってない場合がある*/
#include <string.h> /* for strchr() */
#include <stdlib.h> /* for malloc() */
/* */
#define MAX_F_NAME 40 /* */
#define INDEX_OK 0 /* */
#define RET_OK 0 /* */
#define RET_NG -1 /* */
#define FILE_NG -2 /* */
/* */
int main(void) /* */
{ /* */
FILE *fin; /* 入力元ファイルポインタ */
FILE *fout; /* 出力先ファイルポインタ */
int index = INDEX_OK; /* 異常判定 */
int rtnCode = RET_OK; /* リターンコード */
char fileName[MAX_F_NAME] = ""; /* ファイル名 */
char *desk; /* メモリ作業領域 */
char ans[3] = ""; /* 継続判定 */
char spew = '\0'; /* stdin吐出し */
struct stat st; /* for stat() */
/* */
while(1) /* 無限ループ */
{ /* */
printf("コピー元ファイル名 > "); /* 画面表示 */
fgets(fileName, MAX_F_NAME, stdin); /* 入力元ファイル名の取得 */
if(!strchr(fileName, '\n')) /* 入力判定 */
{ /* 入力が長すぎる */
while(spew != '\n') /* stdin吐出し */
{ /* */
spew = fgetc(stdin); /* */
} /* */
spew = '\0'; /* */
} /* */
/* */
fileName[strlen(fileName) - 1] = '\0'; /* 末尾改行の処理 */
index = stat(fileName, &st); /* システム情報取得 */
if(INDEX_OK != index) /* 異常処理 */
{ /* */
printf("statまたは入力のエラー\n"); /* 画面表示 */
rtnCode = RET_NG; /* リターンコードにエラー設定 */
break; /* 処理終了 */
} /* */
/* */
desk = (char*)malloc(st.st_size); /* 作業領域の確保 */
if(NULL == desk) /* 異常処理 */
{ /* */
printf("mallocでエラー\n"); /* 画面表示 */
rtnCode = RET_NG; /* リターンコードにエラー設定 */
break; /* 処理終了 */
} /* */
/* free(desk);*/ /* * freeするタイミング? */
/* */
fin = fopen(fileName, "rb"); /* 入力元ファイルオープン(バイナリ) */
if(NULL == fin) /* 異常処理 */
{ /* */
printf("fopenでエラー(入力元)\n"); /* 画面表示 */
rtnCode = FILE_NG; /* リターンコードにファイルエラー設定 */
break; /* 処理終了 */
} /* */
/* */
/* fgets(desk, st.st_size, fin); * バイナリモードなら一発可能?不可能! */
fread(desk, sizeof(char), st.st_size, fin); /* これならいける! */
fclose(fin); /* 入力元ファイルクローズ */
/* free(desk);*/ /* * freeするタイミング? */
/* */
printf("コピー先ファイル名 > "); /* 画面表示 */
fgets(fileName, MAX_F_NAME, stdin); /* 出力先ファイル名の取得 */
if(!strchr(fileName, '\n')) /* 入力判定 */
{ /* 入力が長すぎる */
while(spew != '\n') /* stdin吐出し */
{ /* */
spew = fgetc(stdin); /* */
} /* */
spew = '\0'; /* */
} /* */
/* */
fileName[strlen(fileName) - 1] = '\0'; /* 末尾改行の処理 */
fout = fopen(fileName, "ab"); /* 出力先ファイルオープン(バイナリ) */
if(NULL == fout) /* 異常処理 */
{ /* */
printf("fopenでエラー(出力先)\n"); /* 画面表示 */
rtnCode = FILE_NG; /* リターンコードにファイルエラー設定 */
break; /* 処理終了 */
} /* */
/* */
/* fputs(desk, fout); * バイナリモードなら一発可能?不可能! */
fwrite(desk, sizeof(char), st.st_size, fout); /* これならいける! */
fclose(fout); /* 出力先ファイルクローズ */
free(desk); /* * freeするタイミング? */
/* */
printf("コピー成功したかも。まだやる?[Y/n] > "); /* 画面表示 */
fgets(ans, 2, stdin); /* 継続判定の取得 */
if(!strchr(ans, '\n')) /* 入力判定 */
{ /* 入力が長すぎる */
while(spew != '\n') /* stdin吐出し */
{ /* */
spew = fgetc(stdin); /* */
} /* */
spew = '\0'; /* */
} /* */
/* */
if((ans[0] == 'n' || ans[0] == 'N') && (ans[1] == '\n')) /* 継続判定 */
{ /* 継続判定はnかN1文字 */
printf("正常終了\n"); /* 画面表示 */
break; /* 処理終了 */
} /* */
} /* */
/* */
return (rtnCode); /* */
}
*** 修正履歴 [#tad8719c]
Borlandには"unistd.h", "getopt.h"が存在しないため、Maximaの中から強引に入手した。(実は必要なし?)~
取得したファイルサイズを引数としてfread,fwriteを呼出し、一発コピーに成功した。~
~
*** 課題 [#v760a770]
+バイナリでなくテキストモードで同じことをすると、ファイル末尾に"\Comm"なるものが挿入される。
+コンパイル時にfopen_sを使うように警告が出る。
+mallocの仕様をよく理解していないので、free()するベストのタイミングがわからない。~
~
暇があればこれらについても調べてみます。~
~
1.について、Unix環境ではバイナリでの処理が基本のため、モードの違いによって動きが変わることはないみたい。~
2.コンパイラによって出たり出なかったり。~
3.誰か教えて。~
~
-備考~
ファイルロックは自分で実装してください。