ページ 11

ファイル操作に関して

Posted: 2013年4月26日(金) 14:50
by ただの屍のようだ
工場でバイトはじめたせいか、牛歩ペースで独学C++を進んでいます。
演習問題で躓きました。プログラム:スタックを作る。メモリ領域ではなく、ディスクファイルに値を格納すること。

コード:

#include<iostream>
#include<fstream>
#include<iomanip>
using namespace std;

class stack{
	fstream *fio;
	int pos;
public:
	stack(){fio=NULL;pos=0;}
	stack(fstream *p){fio=p;pos=0;}
	~stack(){(*fio).close();}				//->演算子でもよかったのですが、.のほうがわかりやすいと思うのでいつもこういう書き方してます。
	void setFile(fstream *p){fio=p;}
	int pop();
	void push(int);
	void show();
};

int stack::pop(){
	int key;
	pos--;
	(*fio).seekg(pos*sizeof(int),ios::beg);
	(*fio).read((char*)&key,sizeof(int));
	//ここでファイルから値一つ消す操作を入れたいです
	return key;
}

void stack::push(int key){
	(*fio).seekp(0,ios::end);
	(*fio).write((char*)&key,sizeof(int));
	pos++;
};

void stack::show(){
	while(pos)	cout << pop() << endl;
}

int main(){
	fstream fio("stk",ios::in | ios::out | ios::binary);
	if(!fio) return 1;
	stack stk(&fio);
	stk.push(5);	stk.push(23);	stk.push(342);	stk.push(11);
	stk.show();
	return 0;
}
どなたかファイルからデータを消す操作、教えて下さい。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 15:27
by h2so5
ファイルの先頭にスタックの要素数を記録しておき、POPする場合は要素数を減らすだけという方法はどうでしょうか。
あと僕はアロー演算子を使ったほうが分かりやすいと思います。タイプ数も少なくて済みますしね。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 15:31
by ただの屍のようだ
先頭に要素数を入れると、効果にたいして処理がめんどくさいので最終手段として使いたいです。

*おそらく突っ込まれるであろうデストラクターについて:『なにも考えずに勢いで作りました。反省してます。』

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 18:03
by softya(ソフト屋)
少なくとも私はfstreamを極めたわけではないですが、fstreamにファイルサイズを変更するメンバ関数を見たことありません。

> ファイルの先頭にスタックの要素数を記録しておき、POPする場合は要素数を減らすだけという方法はどうでしょうか。

私もスタックをファイル化している意義が薄いと思います。
※ スタックされるのがint限定なら後ろからシークすればposは不要なのでは?とも思います。

> あと僕はアロー演算子を使ったほうが分かりやすいと思います。タイプ数も少なくて済みますしね。

私も*fioよりもfio->を推奨します。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 18:17
by usao
特定のファイルについて, スタック的にしかアクセスしないで編集する ということを実現したいだけなら
既存ファイルを読み込む→メモリ上でスタックとして操作する→操作終了時に現結果をファイルに書き出す
ということをすればいいだけですが,
スタックのイメージを絶対に一時たりともメモリ上に持ちたくない とか
pop()で”実際に消す”ということが譲れない(本当は存在として残っているけど消したことにする的な扱いがゆるされない)
とかいうことであれば,1要素毎に1ファイルを作るとかw

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 20:56
by ただの屍のようだ
ファイルの先頭に要素数を入れると、pop(),push()が頻繁に行われた場合、いちいちカーソルを呼び戻して値をいじるのがめんどくさいです。
あと、ファイルの内容実際に消えるわけではありません(あくまで、書き換えられたり、付け足されたりする)ので。
EOFのsizeof(int)の前でpopすると、常に最後のkeyのみ返されると予想します。

操作として、"\b \b"も考えたのですが、スペースに対する処理がややこしいです。とりあえず、ほかの手段探しながら、次の章へと進みます。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 21:05
by h2so5
ただの屍のようだ さんが書きました:ファイルの先頭に要素数を入れると、pop(),push()が頻繁に行われた場合、いちいちカーソルを呼び戻して値をいじるのがめんどくさいです。
あと、ファイルの内容実際に消えるわけではありません(あくまで、書き換えられたり、付け足されたりする)ので。
EOFのsizeof(int)の前でpopすると、常に最後のkeyのみ返されると予想します。
EOFは関係なくて要素数を元にseekg, seekpをすれば実際にファイルの内容が消えなくてもデータ構造としては同じ事です。
データ構造において、それが保持しているデータの内容と内部的な構造が一致している必要はないと思います。

要素数を書き換えるのが面倒なのは確かですが、閉じるときにだけ書き換える方法もありますね。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 21:08
by softya(ソフト屋)
ただの屍のようだ さんが書きました:ファイルの先頭に要素数を入れると、pop(),push()が頻繁に行われた場合、いちいちカーソルを呼び戻して値をいじるのがめんどくさいです。
あと、ファイルの内容実際に消えるわけではありません(あくまで、書き換えられたり、付け足されたりする)ので。
EOFのsizeof(int)の前でpopすると、常に最後のkeyのみ返されると予想します。
問題は、このスタックファイルがプログラムを終えた時に引き続き中身を使用するのかで設計が変わると言うことでしょう。
そこは無視されるんでしょうか?
ただの屍のようだ さんが書きました: 操作として、"\b \b"も考えたのですが、スペースに対する処理がややこしいです。とりあえず、ほかの手段探しながら、次の章へと進みます。
自己完結されているようですが、ちゃんと説明をお願いします。意味が不明です。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 21:29
by ただの屍のようだ
ファイルをメモリのように使うことが問題の主旨なので、プログラム終了後のデータは破棄します。
破棄しないのであれば、要素数を先頭に保存するべきだと思います。

"\b \b"操作は前の1bitを半角スペースにしてカーソルを1bit前へ戻す。よって
fstream.seekp(0,ios::cur); //シーク操作
for(int i=sizeof(int);i;i--) fstream.write("/b /b"); //カーソル直前のint変数一つをスペースに変える。

そして、スペースを読み飛ばす関数とスペース以外を読み飛ばす関数を用意すればスタックとしては十分機能すると思いますが、もっと簡潔な方法を探しています。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 21:31
by ただの屍のようだ
1bit→1byteでした

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 21:38
by softya(ソフト屋)
ただの屍のようだ さんが書きました:ファイルをメモリのように使うことが問題の主旨なので、プログラム終了後のデータは破棄します。
破棄しないのであれば、要素数を先頭に保存するべきだと思います。
そういう前提なのですね。
ただの屍のようだ さんが書きました: "\b \b"操作は前の1bitを半角スペースにしてカーソルを1bit前へ戻す。よって
fstream.seekp(0,ios::cur); //シーク操作
for(int i=sizeof(int);i;i--) fstream.write("/b /b"); //カーソル直前のint変数一つをスペースに変える。

そして、スペースを読み飛ばす関数とスペース以外を読み飛ばす関数を用意すればスタックとしては十分機能すると思いますが、もっと簡潔な方法を探しています。
エスケープシーケンスでファイル操作可能だと思えないのですが、実験して確認された事項ですか?

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 21:39
by みけCAT
ただの屍のようだ さんが書きました:"\b \b"操作は前の1bitを半角スペースにしてカーソルを1bit前へ戻す。
1bit前に戻すことは考えにくいです。あるとしたら1バイトです。
fstreamがstd::fstreamクラスの変数として、fstream.write("/b /b");はコンパイルエラーになりました。

コード:

D:\(略)\fstreamtest.cpp: In function 'int main()':
D:\(略)\fstreamtest.cpp:10:19: error: no matching function for call to 'std::basic_fstream<char>::write(const char [6])'

D:\kota_documents\Documents\programs\対質問\fstreamtest\fstreamtest.cpp:10:19: note: candidate is:
In file included from c:\mingw4.7.2\bin\../lib/gcc/mingw32/4.7.2/include/c++/ostream:607:0,
                 from c:\mingw4.7.2\bin\../lib/gcc/mingw32/4.7.2/include/c++/istream:41,
                 from c:\mingw4.7.2\bin\../lib/gcc/mingw32/4.7.2/include/c++/fstream:40,
                 from D:\kota_documents\Documents\programs\対質問\fstreamtest\fstreamtest.cpp:1:
c:\mingw4.7.2\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/ostream.tcc:184:5: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::write(const _CharT*, std::streamsize) [with _CharT = char; _Traits = std::char_traits<char>; std::streamsize = int]
c:\mingw4.7.2\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/ostream.tcc:184:5: note:   candidate expects 2 arguments, 1 provided
また、fio.write("/b /b",strlen("/b /b"));としても前のバイトの削除の効果やカーソル(ファイルポインタのことか?)を戻す効果はありません。
fio.write("\b \b",strlen("\b \b"));も同じです。08 20 08というデータが書き込まれるだけです。
int変数は環境によって異なりますが、4バイトのことが多いと思います。1バイトの可能性は低いです。
スペースを4個書き込んだとしても、それは538976288というint型の値になるだけです。(マジックナンバーとして読み飛ばすことはできます)

ファイルポインタをint型のサイズ分前に戻すには、fio.seekg(-(int)sizeof(int),ios_base::cur);が使えます。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 22:09
by ISLe
そもそもpushで問答無用にファイル末尾から書き出しているのはなぜですかね。
現在位置を基準にすればシンプルにできるのでは?


関係ないことですが、ドット演算子のほうに慣れているというのはサンプルプログラムくらいしか触れていないってことですよね。

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 22:26
by ただの屍のようだ
'\b'も手段の一つなのですが、肝心なのは1バイト数値とけしてかぶらない文字があるかどうかです。
だからこそ、ファイルサイズをまずいじろうと思いました。

Cでの現場経験は皆無ですよ

Re: ファイル操作に関して

Posted: 2013年4月26日(金) 22:31
by softya(ソフト屋)
ただの屍のようだ さんが書きました:'\b'も手段の一つなのですが、肝心なのは1バイト数値とけしてかぶらない文字があるかどうかです。
だからこそ、ファイルサイズをまずいじろうと思いました。
Cでの現場経験は皆無ですよ
テキスト(文字)とバイナリ(2進数)を混同しています。
絶対かぶらないコードは存在しませんよ。

※  みけCATさんが16進数で説明しています。

Re: ファイル操作に関して

Posted: 2013年4月27日(土) 00:33
by ただの屍のようだ
やはりpushをいじるのがもっとも簡単のようです。

Re: ファイル操作に関して

Posted: 2013年4月27日(土) 00:34
by softya(ソフト屋)
ただの屍のようだ さんが書きました:やはりpushをいじるのがもっとも簡単のようです。
フォーラムルールに有るのですが、質問したコードは解決コードを提示してください。
ここのルールとなっています。

Re: ファイル操作に関して

Posted: 2013年4月27日(土) 00:41
by ただの屍のようだ
解決したコード載せますよ。(ないほうがましのデストラクターも直しました。)

コード:

#include<iostream>
#include<fstream>
#include<iomanip>
using namespace std;

class stack{
	fstream *fio;
	int pos;
public:
	stack(){fio=NULL;pos=0;}
	stack(fstream *p){fio=p;pos=0;}
	~stack(){fio=NULL;pos=0;}				//->演算子でもよかったのですが、.のほうがわかりやすいと思うのでいつもこういう書き方してます。
	void setFile(fstream *p){fio=p;}
	int pop();
	void push(int);
	void show();
};

int stack::pop(){
	int key;
	pos--;
	(*fio).seekg(pos*sizeof(int),ios::beg);
	(*fio).read((char*)&key,sizeof(int));
	return key;
}

void stack::push(int key){
	(*fio).seekp(pos*sizeof(int),ios::beg);
	(*fio).write((char*)&key,sizeof(int));
	pos++;
}

void stack::show(){
	while(pos)	cout << pop() << endl;
}

int main(){
	fstream fio("stk",ios::in | ios::out | ios::binary);
	if(!fio) return 1;
	stack stk(&fio);
	stk.push(5);	stk.push(23);	stk.push(342);	stk.push(11);
	stk.show();
	fio.close();
	return 0;
}