ファイルの入出力に関して(テキスト)

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
サモサ

ファイルの入出力に関して(テキスト)

#1

投稿記事 by サモサ » 5年前

プログラムをコマンドプロンプト上で実行する際に
テキストファイル名を引数にとり
そのテキストファイルの中身を temp.txt という
テキストファイルにコピーする

というプログラムを作りました。

下にソースコードを記します。

コード:

#include <stdio.h>
#include <stdlib.h>


int main(int argc,char *argv[]){
	
	FILE *from; //コピー元//
	FILE *to;  //コピー先//
	char ch;
	
	if(argc!=2){
		
		printf("<プログラム名><テキストファイル名>\n");
		printf("このように記してください\n");
		exit(1);
		
	}
	
	if((from = fopen(argv[1],"r")) == NULL ){
		
		printf("ファイルを開くことができませんでした\n");
		exit(1);
		
	}
	
	if((to = fopen("temp.txt","w+")) == NULL ){
		
		printf("ファイルを開くことができませんでした\n");
		exit(1);
		
	}
	
	while(!feof(from)){
		
		ch = fgetc(from);
		
		fputc(ch,to);
		
	}
	
	fclose(from);
	fclose(to);
	
	return 0;
	
}
a.txt を引数としてこのプログラムを実行すると
(a.txt の中身は abcdefg という文字列)
temp.txt の中身が abcdefg・となりました。

・という記号はEOF前の最後のヌル文字か何かを
読み込んだのかなと考えています(全く自信はないですが)

いったいこの記号は何を読み込んだものなのでしょうか?

というかテキストファイルに文字を書き込んで保存するときに
最後にいったい何が記されているのでしょうか?(見えない形で)


またこのプログラムを作るにあたって疑問に思ったことを質問させてください。

・ fgetc関数は戻り値を何かの変数に代入しない限り、先に進まないのでしょうか?(一時停止状態になるのでしょうか?)
   
   テキストファイルのバイト数は調べるためにfgetc関数の読み込む位置を前に進めるという機能だけ使おうとして
   何も戻り値を変数に代入しなかったら、プログラムが途中で止まった(エラーというわけではない…と思います)
   ので疑問に思いました

・ ソースコードを書き換えて上書き保存してからコマンドプロンプトで実行しているのに、前のソースコードの結果がでてしまうことがある。

  良ければ具体的にこういう手順を踏んでいるから、前のソースコードの結果が出てしまうんだよ ということをお教えください。

以下の三点について教えてください、お願いします。

OS は windows7
EasyIDECという統合開発環境を使用しています。

アバター
みけCAT
記事: 6252
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: ファイルの入出力に関して(テキスト)

#2

投稿記事 by みけCAT » 5年前

サモサ さんが書きました:いったいこの記号は何を読み込んだものなのでしょうか?

というかテキストファイルに文字を書き込んで保存するときに
最後にいったい何が記されているのでしょうか?(見えない形で)
出力ファイルをバイナリエディタで開いてみるといいかもしれません。
自分はTSXBINというソフトを利用しています。
または、使用しているテキストエディタ(or閲覧ソフト)によっては、
その記号にカーソルを合わせると下に文字コードが表示されるかもしれません。
サモサ さんが書きました:・ fgetc関数は戻り値を何かの変数に代入しない限り、先に進まないのでしょうか?(一時停止状態になるのでしょうか?)
   
   テキストファイルのバイト数は調べるためにfgetc関数の読み込む位置を前に進めるという機能だけ使おうとして
   何も戻り値を変数に代入しなかったら、プログラムが途中で止まった(エラーというわけではない…と思います)
   ので疑問に思いました
「戻り値を何かの変数に代入しない限り、先に進まない」なんていうことは無いはずです。
(C言語の仕様では、実行時に関数からその戻り値が変数に代入されるかを調べるのは非常に難しいと思います)
止まったプログラムを貼っていただけますか?
サモサ さんが書きました:・ ソースコードを書き換えて上書き保存してからコマンドプロンプトで実行しているのに、前のソースコードの結果がでてしまうことがある。

  良ければ具体的にこういう手順を踏んでいるから、前のソースコードの結果が出てしまうんだよ ということをお教えください。
ソースコードを上書き保存しても、そのソースコードをコンパイルしなければ実行ファイルには反映されません。
「コマンドプロンプトで実行している」というのは、EasyIDEC標準の実行機能を使っていないということですか?
最後に編集したユーザー みけCAT on 2014年8月13日(水) 21:36 [ 編集 1 回目 ]
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

初級者
記事: 200
登録日時: 9年前

Re: ファイルの入出力に関して(テキスト)

#3

投稿記事 by 初級者 » 5年前

while文のループをこんな風に書いてみると、
もしかしたらうまくいくかもしれません。

コード:

   while ((ch = fgetc(from)) != EOF) {
       fputc(ch, to);
   }

サモサ

Re: ファイルの入出力に関して(テキスト)

#4

投稿記事 by サモサ » 5年前

返信ありがとうございます。
出力ファイルをバイナリエディタで開いてみるといいかもしれません。
自分はTSXBINというソフトを利用しています。
または、使用しているテキストエディタ(or閲覧ソフト)によっては、
その記号にカーソルを合わせると下に文字コードが表示されるかもしれません。
バイナリエディタですか…調べてみます。
JIS-shiftコードで調べてみたんですが、同じような形が多くて分かりませんでした。
ただ1バイトの文字ということは確かです。(先述のfgetc関数を使うプログラムで調べました)
またprintf関数では表示できない文字でした、そしてprintf関数で文字コードを確認すると -1 と表示されました。
EasyIDECの使っている stdio.h を確認すると #define EOF (-1) となっていました。
つまり初級者さんが示してくれた下のコードでは、この謎の文字を判定したときに
-1 = EOF となりwhileループを抜けるので上手く表示されるのかな…と思いました。

ということはこの文字はEOFを認識するための文字なのか?という推測に至りました。
じゃあfeof関数は本当に読み込むものが無くなるまで 1 を返さないという仕様なんでしょうか?
だからfeof関数では謎の文字が表示された…?
初級者 さんが書きました:while文のループをこんな風に書いてみると、
もしかしたらうまくいくかもしれません。

コード:

   while ((ch = fgetc(from)) != EOF) {
       fputc(ch, to);
   }
「戻り値を何かの変数に代入しない限り、先に進まない」なんていうことは無いはずです。
(C言語の仕様では、実行時に関数からその戻り値が変数に代入されるかを調べるのは非常に難しいと思います)
止まったプログラムを貼っていただけますか?
同じプログラムを作ってみたのですが、問題なく動作しました。
単にコンパイルし忘れて実行してただけかもしれません。
お騒がせしました。
下にソースコードを記します。

コード:

#include <stdio.h>
#include <stdlib.h>


int main(int argc,char *argv[]){
	
	FILE *fp;
	int count=0;
	
	printf("%c",165);
	
	if(argc!=2){
		
		printf("<プログラム名><テキストファイル名>\n");
		printf("このように記してください\n");
		exit(1);
		
	}
	
	if((fp = fopen(argv[1],"r")) == NULL ){
		
		printf("ファイルを開くことができませんでした\n");
		exit(1);
		
	}
	
	
	while(!feof(fp)){
		
		fgetc(fp);
		count++;
	
	}
	
	fclose(fp);
	
	printf("合計%dバイトでした\n",count);
	

	
	return 0;
	
}
ソースコードを上書き保存しても、そのソースコードをコンパイルしなければ実行ファイルには反映されません。
「コマンドプロンプトで実行している」というのは、EasyIDEC標準の実行機能を使っていないということですか?
おっしゃる通りでした。
コンパイルのし忘れで実行ファイルの結果が反映されてないだけでした。
main関数の引数を指定しないプログラムを作る場合(int main(void)の場合)は
EasyIDEC標準の実行機能を使っています。
コマンドプロンプトで実行しているのは、main関数の引数を指定するためです。

サモサ

Re: ファイルの入出力に関して(テキスト)

#5

投稿記事 by サモサ » 5年前

初級者 さんが書きました:while文のループをこんな風に書いてみると、
もしかしたらうまくいくかもしれません。

コード:

   while ((ch = fgetc(from)) != EOF) {
       fputc(ch, to);
   }
確かにそのようにしたら上手くいきました。

while(!feof(from))

との違いはいったい何なんでしょうか?

かずま

Re: ファイルの入出力に関して(テキスト)

#6

投稿記事 by かずま » 5年前

コード:

#include <stdio.h>
 
int main(void)
{
    int i, c;
    FILE *fp = fopen("a.txt", "r");
    if (!fp) return 1;
    for (i = 0;i < 10; i++) {
        printf("feof = %x\n", feof(fp));
        c = fgetc(fp);
        printf("c = %d = %#x\n", c, c);
    }
    return 0;
}
実行結果

コード:

feof = 0
c = 91 = 0x5b
feof = 0
c = 99 = 0x63
feof = 0
c = 111 = 0x6f
feof = 0
c = 100 = 0x64
feof = 0
c = 101 = 0x65
feof = 0
c = 61 = 0x3d
feof = 0
c = 99 = 0x63
feof = 0
c = 93 = 0x5d
feof = 0
c = 10 = 0xa
feof = 0
c = 35 = 0x23
fgetc() は、
1バイト読み込むと、0x00~0xff (0~255) の値を返します。
読み込めないと EOF (-1) を返します。
257種類の値を返すので、
256種類の値しか保持できない char型変数でななく、
intの変数に代入しましょう。

feof() は直前の fgetc() が失敗した場合に 0 でない値を返します。
feof() が 0 だからと言って、これから実行する fgetc() が
EOF を返さないことは保証しません。

かずま

Re: ファイルの入出力に関して(テキスト)

#7

投稿記事 by かずま » 5年前

すみません。
先ほどの実行結果は、a.txt の内容が "abcdefg" ではないものでした。
本当は次のようになります。

コード:

feof = 0
c = 97 = 0x61
feof = 0
c = 98 = 0x62
feof = 0
c = 99 = 0x63
feof = 0
c = 100 = 0x64
feof = 0
c = 101 = 0x65
feof = 0
c = 102 = 0x66
feof = 0
c = 103 = 0x67
feof = 0
c = -1 = 0xffffffff
feof = 10
c = -1 = 0xffffffff
feof = 10
c = -1 = 0xffffffff

サモサ

Re: ファイルの入出力に関して(テキスト)

#8

投稿記事 by サモサ » 5年前

返信ありがとうございます。

fgetcのバイトを読み込む、現在位置について考えていたのですが

コード:

while(!feof(from)){
		ch = fgetc(from);
		fputc(ch,to);
	}
このコードだとまず1行目で
a.txt の1バイト目がファイルの終わりに達しているかどうかを判断する
そして2行目で1バイト目を読み込む
3行目でchの中身を別のファイルにコピーする

まだこの時点では from の現在位置は1バイト目にあるわけですよね?

そしてまた1行目に戻ってきて
また a.txt の1バイト目がファイルの終わりに達しているかどうかを判断する
2行目で現在位置が移動して、2バイト目を読み込む
3行目でchの中身を別のファイルにコピーする

この時点でfrom の現在位置は2バイト目にある

そしてまた1行目に戻ってきて
a.txt の2バイト目がファイルの終わりに達しているかどうかを判断する

…中略

a.txtの abcdefg の g を読み込んで,chの中身を別のファイルにコピー(現在位置7バイト目)

そして1行目に戻ってきて
7バイト目がファイルの終わりに達しているかどうかを判断
そのまま、2行目であるはずのない8バイト目を取得しようとして失敗
ch に EOF(-1) が代入される
3行目で ch の中身が別のファイルにコピー(バイナリエディタで見たら0xFF となってました。補数表現だと思います)

そして1行目に戻ってきて
8バイト目がファイルの終わりに達しているかどうかを判断
whileループを抜けて終了

このような感じでプログラムが進んだため、謎の文字が表示された
ということでしょうか?

Bull
記事: 141
登録日時: 5年前

Re: ファイルの入出力に関して(テキスト)

#9

投稿記事 by Bull » 5年前

ちょっと違うかな

まず fopen() 関数が返すのは FILE * です。どこかに FILE型の
オブジェクト(一般的には構造体)があるわけですが、FILE型には
"ファイル位置表示子"と"ファイル終了表示子"が含まれます。
feof() 関数はこのファイル終了表示子を調べているだけです。
ファイルを読み込んで初めてファイルが終了したかどうかわかるの
で、ファルを読まないうちにファイル終了表示子がセットされるこ
とはありません。

つまり、fgetc()関数が正常終了した場合はファイル終了表示子が
セットされることはないです。

ファイル位置表示子はこれから読もうとしている文字の位置が格納
されています。つまりオープンした時は 0 で一文字読み込んだら
1 になります。(ファイル位置は0から始まります)

また、feof() 関数は読み込み位置を調べているわけではないので
0バイトのファイルをオープンしたとしても、オープン直後は 0 を
返します。(まだファイルの終わりに達していないと言うこと)

サモサ

Re: ファイルの入出力に関して(テキスト)

#10

投稿記事 by サモサ » 5年前

返信ありがとうございます。
fgetc()関数が正常終了せずに、ファイル終了表示子がセットされたときだけ
feof()関数は1を返すという 理解でいいでしょうか?

Bull
記事: 141
登録日時: 5年前

Re: ファイルの入出力に関して(テキスト)

#11

投稿記事 by Bull » 5年前

そうですね。そういう理解でいいと思います。
ただファイルの終わりではなく、何らかのエラーを検出した場合も
fgetc()関数は EOF を返します。このときは、feof()関数は0 を返
し、ferror()関数が非0 を返します。

サモサ

Re: ファイルの入出力に関して(テキスト)

#12

投稿記事 by サモサ » 5年前

分かりました。おかげで理解することができました。
回答してくださった
みけCATさん、初級者さん、かずまさん、Bullさん
ありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る