掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.26848

ファイルポインタ
投稿者---まな(2006/05/14 00:53:43)


#include<stdio.h>

#include"Exam0964.h"

int main(int argc, char *argv[])
{
    int cnt;
    int ret;
    int close_check;
    char yn[YN_SIZE];
    char file_name[STR_SIZE];
    FILE *r_fp;
    FILE *w_fp;

    cnt = 0;
    ret = 0;
    close_check = 0;
    r_fp = NULL;
    w_fp = NULL;

    if(argc != 2)
    {
        fprintf(stderr,"\nUsage : %s FILENAME\n",argv[0]);
        return 1;
    }

    r_fp = fopen(argv[1],"r");
    if(r_fp == NULL)
    {
        fprintf(stderr,"\nSorry, Can't open %s\n",argv[1]);
        return 1;
    }

    while(1)
    {
        fprintf(stdout,"\nOutput filename : ");
        fgets(&file_name[0],sizeof(file_name),stdin);

        for(cnt = 0 ; file_name[cnt] != '\0' ; cnt++)
        {
            if(file_name[cnt] == '\n')
            {
                file_name[cnt] = '\0';
                break;
            }
        }

        w_fp = fopen(&file_name[0],"r");

        if(w_fp != NULL)
        {
            if(fclose(w_fp) == EOF)
            {
                fprintf(stderr,"\nSorry, Can't close      %s\n\n",&file_name[0]);

                if(fclose(r_fp) == EOF)
                {
                    fprintf(stderr,"\nSorry, Can't close %s.\n\n",argv[1]);
                }
return 1;
            }

            fprintf(stdout,"\nWarning! %s is already exist.\n\n",&file_name[0]);

            while(1)
            {
                fprintf(stdout,"Over write? (y or n) : ");
                fgets(&yn[0],sizeof(yn),stdin);

                for(cnt = 0 ; yn[cnt] != '\0' ; cnt++)
                {
                    if(yn[cnt] == '\n')
                    {
                        yn[cnt] = '\0';
                        break;
                    }
                }

                if(yn[0] == 'y' || yn[0] == 'Y' || yn[0] == 'n' || yn[0] == 'N')
                {
                    if(yn[1] == '\0')
                    {
                        break;
                    }
                }

                fprintf(stderr,"Sorry, Input y(Y) or n(N).\n");
            }

            if(yn[0] == 'y' || yn[0] == 'Y')
            {
                if(yn[1] == '\0')
                {
                    break;
                }
            }
        }
        else
        {
            break;
        }
    }

    w_fp = fopen(&file_name[0],"w");
    if(w_fp == NULL)
    {
        fprintf(stderr,"Sorry, Can't open %s.\n",&file_name[0]);

        if(fclose(r_fp) == EOF)
        {
            fprintf(stderr,"Sorry, Can't close %s.\n",argv[1]);
        }
        return 1;
    }

    ret = access_change(w_fp,r_fp);

    if(ret == ERROR)
    {
        fprintf(stderr,"\nWrong in access_change().\n\n");
        return 1 ;
    }

    close_check = CLOSE_OK;

    if(fclose(r_fp) == EOF)
    {
        fprintf(stderr, "\nSorry, Can't close %s.\n",argv[1]);
        close_check = CLOSE_NG;
    }

    if(fclose(w_fp) == EOF)
    {
        fprintf(stderr, "\nSorry, Can't close %s.\n",&file_name[0]);
        close_check = CLOSE_NG;
    }

    if(close_check == CLOSE_NG)
    {
        return 1;
    }
    return 0;
}





実行結果を
>>exam0964 text64

Output filename : text64.a

Warning! text64.a is already exist.
Over write? (y or n) : n

Output filename : text34.b

と、入力したファイルが存在する場合は、
上書きをするかを聞き、noの場合はファイル名を再入力させるようにします。
しかし、作ったプログラムだと
Output filename : Sorry, Can't open
となってしまい、ファイル名を再入力できません。
括弧やbreak、if()の条件式などを変えるなどして、
いろいろと試すなどして、2日間くらい悩んでます。どう直しても同じ結果になってしまいます。
ファイル名を再入力できるようにするには、どのようにすればいいのでしょうか?
入力するものは、
exam0964 text64,text64.a,n,text34.b です。
よろしくお願いします。


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:ファイルポインタ 26850 michi 2006/05/14 01:16:24


No.26850

Re:ファイルポインタ
投稿者---michi(2006/05/14 01:16:24)


私からの提案ですが

ファイルの存在を確認する関数でも作ってみたらどうでしょうか?
以下のような関数です。

1.ファイルを開く(ファイルがなければ失敗するモード)
2.ファイルがあれば即クローズして1(真)を返す
3.ファイルがなければ0を返す

このような関数を作って、プログラムで利用すればかなり見やすいソースになるかと思うのですがどうでしょうか?
(これは私の個人的な意見ですので必要ないと思えば無視してください)


この投稿にコメントする

削除パスワード

No.26851

Re:ファイルポインタ
投稿者---まな(2006/05/14 01:21:05)


書き込みありがとうございます。

ただ、main()の中に、ファイルの存在を確認するプログラムは
書いています。
書いてあるプログラムとは別に存在確認をする関数を作るという事でしょうか?


この投稿にコメントする

削除パスワード

No.26852

Re:ファイルポインタ
投稿者---kafuka(2006/05/14 02:38:47)


適当に作ってみたのですが、結構厄介ですね。

提示されたソースはほとんど読んでいないのですが、

1)引数でファイル名指定
2)指定されたファイルが存在しない場合そのままファイル出力。
3)指定されたファイルが存在する場合、
3−1)上書き確認
3−1−1)上書きOKの場合、そのままファイル出力
3−1−2)上書きNGの場合、ファイル名を再入力。ここで再入力されたファイルについて再チェック。以後2〜を繰り返す。

って感じでしょうか。
特に3−1−2からの再チェックが面倒ですね。
これに不正入力値についてチェックと再入力を入れるとさらに面倒ですね。

こういった場合、ループなどを駆使して上手く作成するのがきれいなのでしょうが、
単純に特定機能を関数化して繰り返し使うのも一つの手です。

繰り返し使う部分を関数化してまとめれば、
多少ループ・分岐のネストが深く複雑になろうがそれほど見にくいソースにはならないってことですかね。


ちなみに、michiさんが言っているチェック関数とは↓のようなものかな?

int CheckFile(char* filename)
{
    FILE *fp;

    fp = fopen(filename, "r");
    if (fp == NULL) {
        return 0;
    }
    fclose(fp);

    return 1;
}



この投稿にコメントする

削除パスワード

No.26854

Re:ファイルポインタ
投稿者---まな(2006/05/14 02:59:39)


ありがとうございます。

いろいろと制限があるので、関数は作れないんです。



この投稿にコメントする

削除パスワード

No.26869

Re:ファイルポインタ
投稿者---kafuka(2006/05/15 17:44:45)


参考。

#include <stdio.h>
#include <string.h>

#define DEF_FILE    "data.txt"    // 引数のかわり
#define MAX_DATA    32

int main(void)
{
    int fileflg;
    char filename[MAX_DATA];
    char keyinput[MAX_DATA];
    FILE *fp_w;
    FILE *fp_c;

    strcpy(filename, DEF_FILE);

    // ファイルチェック
    fileflg = 0;
    while (!fileflg) {

        fp_c = fopen(filename, "r");
        if (fp_c == NULL) {
            fileflg = 1;
            break;
        }
        fclose(fp_c);

        while (1) {
            printf("%s : over write OK? (y or n) : ", filename);
            scanf("%s", keyinput);
            if (keyinput[0] == 'y') {
                fileflg = 1;
                break;
            } else if (keyinput[0] == 'n') {
                printf("input file name : ");
                scanf("%s", filename);
                break;
            }
            printf("input is invalid.\n");
        }

    }

    // ファイル書込み
    fp_w = fopen(filename, "w");
    if (fp_w == NULL) {
        printf("file open error. %s\n", filename);
        return -1;
    }
    fprintf(fp_w, "file write test.\n");
    fclose(fp_w);

    return 0;
}



この投稿にコメントする

削除パスワード

No.26863

Re:ファイルポインタ
投稿者---michi(2006/05/15 01:06:02)


>ちなみに、michiさんが言っているチェック関数とは↓のようなものかな?
>
>
int CheckFile(char* filename)
{
    FILE *fp;

    fp = fopen(filename, "r");
    if (fp == NULL) {
        return 0;
    }
    fclose(fp);

    return 1;
}


その通りです。でも関数はもう追加できないんですね〜。
なんせ私の実力ではあのコメントのないコードを読んで理解するのはちょっと酷だったもんで...。


この投稿にコメントする

削除パスワード

No.26853

Re:ファイルポインタ
投稿者---RAPT(2006/05/14 02:47:12)


ソースを掲載する際には、「コンパイル可能な事象を再現できるコード」
を掲載した方が解決が早くなります。

特に YN_SIZE と STR_SIZE の定義が無いため、精査できません。
疑わしいのは、YN_SIZE が 1 ではないか、という点です。

それと、すべてを main() 関数に押し込むのは、得策とは思えません。
また、

for(cnt = 0 ; file_name[cnt] != '\0' ; cnt++) { if(file_name[cnt] == '\n') { file_name[cnt] = '\0'; break; } }
これを下記のように書かないのは何か理由でもあるのでしょうか。 int len = strlen(file_name); if( file_name[len - 1] == '\n' ) { file_name[len - 1] = '\0' } 同様に、
while(1) { fprintf(stdout,"Over write? (y or n) : "); fgets(&yn[0],sizeof(yn),stdin); for(cnt = 0 ; yn[cnt] != '\0' ; cnt++) { if(yn[cnt] == '\n') { yn[cnt] = '\0'; break; } } if(yn[0] == 'y' || yn[0] == 'Y' || yn[0] == 'n' || yn[0] == 'N') { if(yn[1] == '\0') { break; } } fprintf(stderr,"Sorry, Input y(Y) or n(N).\n"); }
これも下記のように書けます。 while(1) { printf("Over write? (y or n) : "); fgets(yn,sizeof(yn),stdin); if( strchr("yYnN", yn[0]) != NULL ) { break; } fprintf(stderr,"Sorry, Input y(Y) or n(N).\n"); } いずれも間違いではありませんが、冗長に思えます。 # ynの入力が1文字のみと限定したいなら、次の条件を追加すればよい。 # && (yn[1] == '\n' || yn[1] == '\0')



この投稿にコメントする

削除パスワード

No.26855

Re:ファイルポインタ
投稿者---まな(2006/05/14 03:13:35)


ありがとうございます。

YN_SIZEは自作ヘッダーファイルで定義しています。1ではありません。
問題は「コマンドパラメータで指定されたファイルの最後の5行を、入力した出力ファイルに書き出すプログラム」です。
最後の5行を書き出すプログラムは、access_change()で行っています。


***access_change()***
#include <stdio.h>
#include"Exam0964.h"

int access_change(FILE *w_fp,FILE *r_fp)
{
    int cnt;
    int r_str;
    char str[STR_SIZE];
    
    cnt = 0;
    r_str = 0;
    
    if(w_fp == NULL || r_fp == NULL)
    {                       
        return ERROR;
    }
    
    fseek(r_fp,-1,SEEK_END);
    
    cnt=0;
    
    do
    {
        r_str = getc(r_fp);
        if(r_str == '\n')
        {
            fseek(r_fp,-1,SEEK_CUR);
            cnt++;
        }
        
        if(cnt == 5)
        {
            break;
        }
    }while(!fseek(r_fp,-2,SEEK_CUR));
    
    fseek(r_fp,1,SEEK_CUR);
    
    while((fgets(&str[0],sizeof(str),r_fp)) != NULL)
    {
        fputs(&str[0],w_fp);
    }
    
    return SUCCESS;
}



***ヘッダーファイル***
<pre>#ifndef _EXAM0964_MAIN_H_
#define _EXAM0964_MAIN_H_

<font color="#009900">/***** マクロ名定義 *****/</font>

#define STR_SIZE 256 <font color="#009900">/*文字列サイズ *\
/
#define YN_SIZE 2 <font color="#009900">/*上書き確認サイズ *\
/
#define CLOSE_OK 1 <font color="#009900">/* 引数異常の場合の判定値 *\
/
#define CLOSE_NG -1 <font color="#009900">/*クローズ処理失敗の値 *\
/
#define SUCCESS 0 <font color="#009900">/* 正常とみなされる場合の判定値 *\
/
#define ERROR -2 <font color="#009900">/*異常終了 *\
/



<font color="#009900">/***** 関数プロトタイプ宣言 *****/</font>

int access_change(FILE *, FILE *); <font color="#009900">/*アクセス位置移動関数 *\
/


#endif
</pre>

いろいろと制限があって、関数はmain()とacess_change()の2つしか使えません。

書き込んでからもいろいろと試してますが、未だに正しく動かず、原因がまったくわかりません。

よろしくお願いします。


この投稿にコメントする

削除パスワード

No.26858

Re:ファイルポインタ
投稿者---shu(2006/05/14 12:05:37)


CLOSE_OKなりCLOSE_NGっていうのは、
fclose()が成功したかしないかではなくて、
fclose()を使うタイミングのためのものではないでしょうか?

CLOSE_OKなら、もうファイル閉じていいよ〜〜
CLOSE_NGなら、まだファイル閉じちゃ駄目だよ〜〜

あとは、&str[0]のような表現がありますが、strでいいです。

fgets()から取得した文字列末の'\n'は、strtok(str, "\n")としてもOK。


この投稿にコメントする

削除パスワード

No.26866

Re:ファイルポインタ
投稿者---RAPT(2006/05/15 02:21:23)


ソースのうち、下記2点を修正すれば、とりあえず動作はするはずです。
(1) '\0' チェックは重複しているので不要、むしろ、fclose() が漏れている。
if(yn[0] == 'y' || yn[0] == 'Y') { if(yn[1] == '\0') { break; } }
これを下記に変更。 if(yn[0] == 'y' || yn[0] == 'Y') { fclose(w_fp); } (2) バッファが小さすぎる。
#define YN_SIZE 2
これを下記に変更。 #define YN_SIZE 3



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧