#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINE_SIZE 256
#define CMD_SIZE 100
void temporary_save(FILE *txtfile , FILE *temp);
void execmd(FILE *txt, FILE *temp, FILE *undo, char *txtname, char *undo_text, char *temp_cmd, int *p);
void display_all(FILE *txt);
void make_undofile(FILE *txt, FILE *temp, FILE *undo, char *txtname, char *undo_text, char *temp_cmd, int *p);
void one_row_edit(FILE *original, FILE *temp, FILE *undo, int row, char *original_name, char *undo_text, int *p);
void save_end(int *p);
void not_save(FILE *txt, FILE *temp, char *txtname, int *p);
void un_do(FILE *txt, FILE *undo, char *txtname, char *undo_text);
void word_search(FILE *txt, char *txtname);
void word_replace1(FILE *txt, char *txtname);
void line_delete(FILE *txt, char *txtname);
void line_insert(FILE *txt, char *txtname);
int main(int argc, char *argv[]){
FILE *txtfile, *temp, *undo;
int start = 0;
int coe = 1;//終了条件(Condition Of End)
int *p_coe = &coe; //coeへのポインタ
char cmd[CMD_SIZE]; //コマンド
char temp_text[L_tmpnam],undo_text[L_tmpnam]; //一時的ファイルのファイル名(編集前とundo用のデータ保存用)
tmpnam(temp_text); //一時的ファイルのファイル名の決定
tmpnam(undo_text); //undo用ファイル名の決定
//ファイルの引数を調べる。
if(argc != 2){
printf("引数にはファイルのパスを指定してください\n");
exit(1);
}
//終了するまで無限ループ
while(coe){
//ファイルが開けるかどうかを調べる。
if((txtfile = fopen(argv[1], "r")) == NULL){
printf("ファイルを開くことが出来ません");
exit(1);
}
//*を表示する
if(start) printf("*");
else{
//変更せずに終了するときに使用する変更前のファイルの保存
temp = fopen(temp_text, "w");
temporary_save(txtfile,temp);
temp = freopen(temp_text,"r",temp); //w→rに切り替える
txtfile = fopen(argv[1], "r"); //もう一回txtfileと読み込んだファイルを結びつける。
printf("ファイルを終わりまで読み込みました\n*");
start++;
}
//コマンドを入力
fgets(cmd, CMD_SIZE, stdin);
//コマンドの実行
execmd(txtfile, temp, undo, argv[1], undo_text, cmd, p_coe);
//ファイルを閉じる
fclose(txtfile);
}
fclose(temp);
remove(temp_text); //一時的ファイルを削除する
remove(undo_text); //undo用ファイルを削除する
return 0;
}
//一時的ファイルの保存
void temporary_save(FILE *txtfile,FILE *temp){
int c; /* 文字を読み込む */
/* ファイルから EOFが現れるまで1字読み込む */
while ((c = getc(txtfile)) != EOF) {
fputc(c,temp);
}
/* この時点でtxtfileに結びつけられたファイルは最後まで読み込まれてしまったので閉じる */
fclose(txtfile);
}
//機能はここに追加
void execmd(FILE *txt, FILE *temp, FILE *undo, char *txtname, char *undo_text, char *temp_cmd, int *p){
if(strcmp(temp_cmd, "l\n") == 0 || strcmp(temp_cmd, "L\n") == 0) display_all(txt); //すべて表示
if(atoi(temp_cmd) != 0) make_undofile(txt, temp, undo, txtname, undo_text, temp_cmd, p); //一行編集に入る前にundo用ファイルに記録
if(strcmp(temp_cmd, "e\n") == 0 || strcmp(temp_cmd, "E\n") == 0) save_end(p); //保存して終了
if(strcmp(temp_cmd, "n\n") == 0 || strcmp(temp_cmd, "N\n") == 0) not_save(txt,temp,txtname,p); //保存しないで終了
if(strcmp(temp_cmd, "u\n") == 0 || strcmp(temp_cmd, "U\n") == 0) un_do(txt,undo,txtname,undo_text); //前回の編集前の状態に戻す(undo)
if(strcmp(temp_cmd, "s\n") == 0 || strcmp(temp_cmd, "S\n") == 0) word_search(txt,txtname); //単語検索
if(strcmp(temp_cmd, "r\n") == 0 || strcmp(temp_cmd, "R\n") == 0) make_undofile(txt, temp, undo, txtname, undo_text, temp_cmd, p);//置換に入る前にundo用ファイルに記録
if(strcmp(temp_cmd, "d\n") == 0 || strcmp(temp_cmd, "D\n") == 0) make_undofile(txt, temp, undo, txtname, undo_text, temp_cmd, p);//行削除に入る前にundo用ファイルに記録
if(strcmp(temp_cmd, "i\n") == 0 || strcmp(temp_cmd, "I\n") == 0) make_undofile(txt, temp, undo, txtname, undo_text, temp_cmd, p);//行挿入に入る前にundo用ファイルに記録
return;
}
//以下は拡張した関数群
//すべての行を表示
void display_all(FILE *txt){
char str[LINE_SIZE]; //行保存用の配列
int i = 1; //行番号
while(!feof(txt)){
if(fgets(str, LINE_SIZE - 1, txt) == NULL) strcpy(str, "\n");
if(ferror(txt)){
printf("ファイルの読み込み失敗\n");
break;
}
if(!feof(txt)) printf("%8d: %s", i, str);
else{
if(strcmp(str, "\n") != 0) printf("%8d: %s\n", i, str);
}
i++;
}
return;
}
//undo用ファイルの保存
void make_undofile(FILE *txt, FILE *temp, FILE *undo, char *txtname, char *undo_text, char *temp_cmd, int *p){
char c; //txt→undoファイル用
undo = fopen(undo_text,"w");
while( (c = fgetc(txt)) != EOF ) //編集直前の状態を保存する
{
fputc(c,undo);
}
txt = freopen(txtname,"r",txt); //もう一度txtをオープンしておく
fclose(undo); //undoは閉じておく。
if(atoi(temp_cmd) != 0) one_row_edit(txt, temp, undo, atoi(temp_cmd), txtname, undo_text, p); //一行編集
if(strcmp(temp_cmd, "r\n") == 0 || strcmp(temp_cmd, "R\n") == 0) word_replace1(txt,txtname); //置換
if(strcmp(temp_cmd, "d\n") == 0 || strcmp(temp_cmd, "D\n") == 0) line_delete(txt,txtname); //行削除
if(strcmp(temp_cmd, "i\n") == 0 || strcmp(temp_cmd, "I\n") == 0) line_insert(txt,txtname); //行挿入
}
//一行編集
void one_row_edit(FILE *original, FILE *temp, FILE *undo, int row, char *original_name, char *undo_text, int *p){
FILE *shadow; //実際に行を追加していくファイル(あとで元ファイルと入れ替える)
char shadow_name[L_tmpnam]; //一行編集時のデータ保存用
char str[LINE_SIZE]; //行保存用の配列
char c; //shadow→original用
int i; //for用の変数
tmpnam(shadow_name); //一時的ファイルのファイル名の決定
//rowが0以下のとき
if(row <= 0){
printf("行番号が不正です。\n");
return;
}
shadow = fopen(shadow_name, "w");
//目的の行の手前まで読み込み書き込む
for(i = 1; i < row; i++){
fgets(str, LINE_SIZE - 1, original);
if(ferror(original)){
printf("行読み込みエラー\n");
fclose(shadow);
remove(shadow_name);
return;
}
if(feof(original)){
printf("行番号が不正です。\n");
fclose(shadow);
remove(shadow_name);
return;
}
fputs(str, shadow);
}
//行番号と行を表示
fgets(str, LINE_SIZE - 1, original);
if(feof(original)) printf("%8d: %s\n", row, str);
else
printf("%8d: %s", row, str);
//新たな行の書き込み
printf("%8d: ", row);
fgets(str, LINE_SIZE - 1, stdin);
fputs(str, shadow);
//終わりまで読み込む(display_allと同じように)
while(!feof(original)){
if(fgets(str, LINE_SIZE - 1, original) == NULL) strcpy(str, "\n");
if(ferror(original)){
printf("行読み込みエラー\n");
return;
}
if(!feof(original)) fputs(str, shadow);
else{
if(strcmp(str, "\n") != 0) fputs(str, shadow);
}
}
//shadowをoriginalに置き換える
/*fclose(original);
fclose(shadow);
if(remove(original_name) == -1){
printf("ファイルの削除に失敗しました。終了します。\n");
execmd(original, temp, undo, original_name, undo_text, "n\n", p);
return;
}
if(rename(shadow_name, original_name) == -1){
printf("ファイルのリネームに失敗しました。終了します。\n");
execmd(original, temp, undo, original_name, undo_text, "n\n", p);
return;
}
//main関数でfcloseをしているため、もう一度開く
if((original = fopen(original_name, "r")) == NULL){
printf("ファイルを開くことが出来ません");
exit(1);
}*/
//shadow→original
freopen(original_name, "w", original);
freopen(shadow_name, "r", shadow);
while(!feof(shadow)){
c = fgetc(shadow);
if(ferror(shadow)){
fclose(shadow);
printf("一行編集shadow→original読み込みエラー\n");
execmd(original, temp, undo, original_name, undo_text, "n\n", p);
return;
}
if(!feof(shadow)) fputc(c, original);
if(ferror(original)){
fclose(shadow);
printf("一行編集shadow→original書き込みエラー\n");
execmd(original, temp, undo, original_name, undo_text, "n\n", p);
return;
}
}
fclose(shadow);
if(remove(shadow_name) == -1) printf("一行編集shadow削除エラー\n");
return;
}
//セーブして終了
void save_end(int *p){
*p = 0;
return;
}
//セーブしないで終了
void not_save(FILE *txt, FILE *temp,char *txtname, int *p){
int c; /* 文字を読み込む */
txt = freopen(txtname,"w",txt); //読み込みから書き込みへ
/* ファイルから EOFが現れるまで1字読み込む */
while ((c = fgetc(temp)) != EOF) { //最初に保存してた内容を読み込み
fputc(c,txt); //元のファイルに戻す
}
*p = 0;
return;
}
//一つ前の状態に戻す(undo)
void un_do(FILE *txt, FILE *undo, char *txtname, char *undo_text){
char c; // 文字を読み込む
if((undo = fopen(undo_text,"r"))==NULL)
{
printf("まだ編集は行われておりません\n");
return;
}
txt = freopen(txtname,"w",txt); //読み込みから書き込みへ
/* ファイルから EOFが現れるまで1字読み込む */
while((c = fgetc(undo)) != EOF){ //一つ前の状態の内容を読み込み
fputc(c,txt); //ファイルに入れて戻す
}
fclose(undo);
freopen(txtname,"r",txt);
return;
}
//単語検索
void word_search(FILE *txt, char *txtname)
{
int i , j , k , l = 0 ;
int line[1000] ; //単語出現数は1000回まで
char str[LINE_SIZE], word[CMD_SIZE];
fgets(word, CMD_SIZE, stdin); //単語のサイズは99まで
for(i=1;!feof(txt);i++) //1行読み込む毎にiをカウント
{
fgets(str,LINE_SIZE,txt); //まず一行全部読み込む
for(j=0;str[j]!='\n';j++) //読み込んだ行の中に単語が含まれていないか探す
{
if( str[j] == EOF )
break;
for(k=j;word[k-j]==str[k];k++)
{
if(word[k-j+1]=='\n') //含まれていたら
{
line[l] = i ; //その行番号をline[]に入れる
l++;
}
}
}
for(k=0;k<j;k++)
{
str[k] = '\0' ;
}
}
txt = freopen(txtname,"r",txt);
//line[]に入れた行番号からその行を出力する
for(i=0;i<l;i++)
{
if( i == 0 )
{
for(j=1;j<=line[0];j++)
fgets(str,LINE_SIZE,txt);
k = line[0];
printf("%d : %s",line[0],str);
if( feof(txt) )
{
printf("\n");
}
}
else if( line[i] != line[i-1] ) //1行にいくつもある場合はとばす
{
for(j=k+1;j<=line[i];j++)
fgets(str,LINE_SIZE,txt);
k = line[i];
printf("%d : %s",line[i],str);
if( feof(txt) )
{
printf("\n");
}
}
}
}
//置換
void word_replace1(FILE *txt, char *txtname)
{
int i , j , k , l = 0 , h , n , m , g , LAST_EOF_LINE = 0;
int line[1000] ; //単語出現数は1000回まで
char str[LINE_SIZE], word[CMD_SIZE], word2[CMD_SIZE], c, shadow_name[L_tmpnam];
FILE *shadow ;
tmpnam(shadow_name);
fgets(word, CMD_SIZE, stdin); //単語のサイズは99まで
for(i=1;c != EOF;i++) //1行読み込む毎にiをカウント
{
if((c = fgetc(txt)) == EOF ) //その行の一文字目がEOFなら
{
break ; //終了
}
else if( c == '\n' ) //その行が単なる改行なら
{
j = 0 ;
str[j] = '\n' ; //改行をstrにおさめて
goto input1 ; //読み込み作業は飛ばす
}
str[0] = c ; //一文字目がEOFでも改行でもなかったら
for(j=1;(c = fgetc(txt)) != '\n';j++) //改行するまでその行を読み込む
{
str[j] = c ;
if( c == EOF ) //ファイルの最後が改行せずに終わっていたら
{
goto input1; //改行を入れることなく飛ばす
}
}
str[j] = '\n' ; //改行をstrに入れる
input1 : ;
str[++j] = '\0' ; //最後にNULL文字を入れる
for(j=0;str[j]!='\n';j++) //読み込んだ行の中に単語が含まれていないか探す
{
if( str[j] == EOF )
break;
for(k=j;word[k-j]==str[k];k++)
{
if(word[k-j+1]=='\n') //含まれていたら
{printf("1st %d\n",i);
line[l] = i ; //その行番号をline[]に入れる
l++;
}
}
}
for(k=0;k<j;k++)
{
str[k] = '\0' ;
}
}
txt = freopen(txtname,"r",txt);
//ここから、ファイル内容をshadowに置き換える
fgets(word2,CMD_SIZE,stdin);
shadow = fopen(shadow_name,"w");
for(i=0;i<l;i++) //置換する前の単語のある行の手前まで
{
if( i == 0 )
{
for(j=1;j<line[0];j++)
{
fgets(str,LINE_SIZE,txt);
fputs(str,shadow); //内容をshadowにコピー
}
m = line[0] ;
}
else if( line[i] != line[i-1] ) //1行に複数ある場合は一度だけ
{
for(j=m+1;j<line[i];j++)
{
fgets(str,LINE_SIZE,txt);
fputs(str,shadow);
}
m = line[i] ;
}
else
{
goto END1 ;
}
//置換する前の単語のある行にきたら
fgets(str,LINE_SIZE,txt); //strにその行をコピー
for(j=0,h=0;str[h+j]!='\n';j++) //行の中の単語が含まれてる位置を探す
{
if( str[h+j] == EOF )
break;
for(k=h+j;word[k-(h+j)]==str[k];k++)
{
if(word[k-(h+j)+1]=='\n') //含まれていたら
{
for(g=h;g<h+j;g++) //その単語の手前までshadowにコピー
{
fputc(str[g],shadow);
}
for(n=0;word2[n]!='\n';n++) //置換した後の単語をshadowにコピー
{
fputc(word2[n],shadow);
}
h = k + 1;
j = -1 ;
break ;
}
}
}
//もうその行に置換する前の単語が無ければ
for(k=h;str[k]!='\n' && str[k]!=EOF;k++)//残りをshadowにコピー
{
fputc(str[k],shadow);
}
if(str[k] == EOF ) //もし行の最後が改行でなくEOFで終わっていたら
{
LAST_EOF_LINE = 1 ; //最後の行は改行でなくEOFで終わっていることをマークし
goto END2 ; //飛ぶ
}
fputc('\n',shadow); //そうでなければ行の最後は改行を入れる
END1 : ;
}
//もうファイル中のそれ以降の行に置換する前の単語が無ければ
c = 1 ; //最初のc == EOF を避けるため
for(;c != EOF;)
{printf("last\n");
if((c = fgetc(txt)) == EOF ) //その行の一文字目がEOFなら
{printf("%c\n",c);
break ; //終了
}
else if( c == '\n' ) //その行が単なる改行なら
{
j = 0 ;
str[j] = '\n' ; //改行をstrにおさめて
goto input2 ; //読み込み作業は飛ばす
}
str[0] = c ; //一文字目がEOFでも改行でもなかったら
for(j=1;(c = fgetc(txt)) != '\n';j++) //改行するまでその行を読み込む
{
str[j] = c ;
if( c == EOF ) //ファイルの最後が改行せずに終わっていたら
{
goto input2; //改行を入れることなく飛ばす
}
}
str[j] = '\n' ; //改行をstrに入れる
input2 : ;
str[++j] = '\0' ; //最後にNULL文字を入れる
fputs(str,shadow);
}
END2 : ;
txt = freopen(txtname,"w",txt); //両方のファイルをオープンし直して
shadow = freopen(shadow_name,"r",shadow);
/* 今回の行編集プログラミングにおいて最も問題となったのがファイルの終わり方で
改行せずにファイルが終了する場合に多くの注意が必要となった。
すなわち
new\n
\n
old\n
というファイルは改行の後にファイルが終了しているが
new\n
\n
old
では行末にEOFが来ることになる。こうした場合単にfgetsで行を読み取ると、最後にEOFの部分が記号で表示されてしまう
という問題が生じた。そのため多くの場合一文字ずつ読み込んで書き込みを行う手法を取ってきたが、この置換関数では行末に
EOFがあるファイルで、更にその最後の行を置換する場合、一文字ずつ読み込んで書き込みを行おうとすると何故かshadowファイル
に謎の記号が後に続いてしまい書き込む元のファイルデータにそれを写してしまうことになった。ところがこのケースでfgetsに
よる読み込みを行うと今度はEOFを記号として書き込むことなくうまく写し取ることに成功できるようになった。そこで今回は
最後の写しを行う段階で場合分けを行っている
(説明が無いとあまりにも無駄な行動をしてるとしか思えないのでわざわざ記しておくことにしました)
*/
if( LAST_EOF_LINE == 0 )
{
for(;(c = fgetc(shadow)) != EOF;) //shadowの中身をtxtにコピー
{
for(;c != '\n' && c != EOF ;c = fgetc(shadow))
{
fputc(c,txt);
}
fputc('\n',txt);
}
}
else
{
for(;fgets(str,LINE_SIZE - 1,shadow) != NULL;) //shadowの中身をコピー
{
fputs(str,txt);
}
}
fclose(shadow);
remove(shadow_name);
return;
}
//行削除
void line_delete(FILE *txt, char *txtname)
{
int i , j ;
FILE *shadow;
char c = 1, line[10], str[LINE_SIZE], shadow_name[L_tmpnam] ;
tmpnam(shadow_name);
fgets(line,9,stdin); //削除したい行を入力してもらう
if( atoi(line) < 1 )
{
printf("行番号が不正です。\n");
return;
}
shadow = fopen(shadow_name,"w");
for(i=1;c != EOF;i++)
{
if((c = fgetc(txt)) == EOF ) //その行の一文字目がEOFなら
{
break ; //終了
}
else if( c == '\n' ) //その行が単なる改行なら
{
j = 0 ;
str[j] = '\n' ; //改行をstrにおさめて
goto input ; //読み込み作業は飛ばす
}
str[0] = c ; //一文字目がEOFでも改行でもなかったら
for(j=1;(c = fgetc(txt)) != '\n';j++) //改行するまでその行を読み込む
{
str[j] = c ;
if( c == EOF ) //ファイルの最後が改行せずに終わっていたら
{
goto input; //改行を入れることなく飛ばす
}
}
str[j] = '\n' ; //改行をstrに入れる
input : ;
str[++j] = '\0' ; //最後にNULL文字を入れる
if( i != atoi(line) ) //削除しない行はshadowにコピー
{
fputs(str,shadow);
}
//削除する行はコピーせずに読み飛ばす
}
if( atoi(line) >= i )
{
printf("行番号が不正です。\n");
}
txt = freopen(txtname,"w",txt); //両方のファイルをオープンし直して
shadow = freopen(shadow_name,"r",shadow);
for(;(c = fgetc(shadow)) != EOF;) //shadowの中身をtxtにコピー
{
for(;c != '\n' && c != EOF ;c = fgetc(shadow))
{
fputc(c,txt);
}
fputc('\n',txt);
}
fclose(shadow);
remove(shadow_name);
return;
}
//行挿入
void line_insert(FILE *txt, char *txtname)
{
//ほぼ行削除と同じ構造
int i , j ;
FILE *shadow;
char c = 1, line[10], str[LINE_SIZE], shadow_name[L_tmpnam] ;
tmpnam(shadow_name);
fgets(line,9,stdin); //挿入したい行の手前の行を入力してもらう。
if( atoi(line) < 0 )
{
printf("行番号が不正です。\n");
return;
}
shadow = fopen(shadow_name,"w");
if( atoi(line) == 0 ) //もし1行目に挿入したい場合は先に入力をする
{
fgets(str,LINE_SIZE,stdin); //挿入する内容を入力
fputs(str,shadow); //それをコピー
} //あとは2行目以降に挿入したい場合と同じ動きをする
for(i=1;c != EOF;i++)
{
if((c = fgetc(txt)) == EOF ) //その行の一文字目がEOFなら
{
break ; //終了
}
else if( c == '\n' ) //その行が単なる改行なら
{
j = 0 ;
str[j] = '\n' ; //改行をstrにおさめて
goto input ; //読み込み作業は飛ばす
}
str[0] = c ; //一文字目がEOFでも改行でもなかったら
for(j=1;(c = fgetc(txt)) != '\n';j++) //改行するまでその行を読み込む
{
str[j] = c ;
if( c == EOF ) //ファイルの最後が改行せずに終わっていたら
{
goto input; //改行を入れることなく飛ばす
}
}
str[j] = '\n' ; //改行をstrに入れる
input : ;
str[++j] = '\0' ; //最後にNULL文字を入れる
if( i != atoi(line) ) //挿入する手前以外の行は普通にshadowにコピー
{
fputs(str,shadow);
}
else //挿入する手前の行は
{
fputs(str,shadow); //まず手前の行の内容をコピー
fgets(str,LINE_SIZE,stdin); //挿入する内容を入力
fputs(str,shadow); //それをコピー
//通常の動きに戻る
}
}
if( atoi(line) >= i )
{
printf("行番号が不正です。\n");
}
txt = freopen(txtname,"w",txt); //両方のファイルをオープンし直して
shadow = freopen(shadow_name,"r",shadow);
for(;(c = fgetc(shadow)) != EOF;) //shadowの中身をtxtにコピー
{
for(;c != '\n' && c != EOF;c = fgetc(shadow))
{
fputc(c,txt);
}
fputc('\n',txt);
}
fclose(shadow);
remove(shadow_name);
return;
}
最終更新:2007年07月28日 23:19