fstreamの効率のいい使い方

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

fstreamの効率のいい使い方

#1

投稿記事 by AC » 16年前

どうも御世話になっております。
とりあえずソースからいきます。
#include <tchar.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <fstream>

// tchar.h 的に使えるようにする
#ifdef _UNICODE
#define _tcout				std::wcout
#define _tstring			std::wstring
#define _tfstream			std::wfstream
#else
#define _tcout				std::cout
#define _tstring			std::string
#define _tfstream			std::fstream
#endif 

// prototype
void InsertText(_tstring dir, DWORD pos, _tstring txt);

// main
int _tmain(int argc, TCHAR* argv[/url], TCHAR* envp[/url])
{
	std::locale loc = std::locale();
	loc = std::locale(loc, "japanese", std::locale::ctype);
	std::locale::global(loc);
	
	// テキストを任意の位置で追記
	InsertText(_T("書き出し.txt"), 6, _T("test"));
	
	return 0;
}

void InsertText(_tstring dir, DWORD pos, _tstring txt)
{
	// ファイルストリーム
	_tfstream fs(dir.c_str(), std::ios::in | std::ios::out);
	
	// pos
	_tfstream::pos_type insert_pos = pos;
	
	// 一時的な保持用
	_tstring str1, str2, tmp;
	
	while(true)
	{
		fs >> tmp;
		str1 += tmp;
		
		if(fs.tellg() > insert_pos)
		{
			// pos位置までに削る
			if(str1.size() > pos)
			{
				// char
				str1 = str1.substr(0, pos);
			}
			else
			{
				// wchar_t
				str1 = str1.substr(0, (DWORD)(pos/2));
			}
			
			// pos位置へ移動
			fs.seekg(insert_pos);
			while(!fs.eof())
			{
				// 終わりまで読み込む
				fs >> tmp;
				str2 += tmp;
			}
			break;
		}
	}
	
	// debug
	_tcout << str1 << txt << str2 << std::endl;
	
	// 最初へ移動
	fs.seekp(0, std::ios::beg);
	fs << str1 << txt << str2 << std::endl;
}
少々長くなりましたが、テキストを任意の位置に追加(挿入)するようなプログラムを書いたつもりなのですが、debugと書いたcoutによる出力では、ちゃんとできていることが確認できるのですが、最後のfsによる書き込みが正常に出来ていないようです。
どこに問題があるのでしょうか?

それと、途中に挿入するような場合はわざわざこんな風にしないといけないのでしょうか?
もっと簡単にはできないのでしょうか(std::ios::appだと最後にしか書き込めないので…)?
効率のいい使い方等ありましたら、よろしくお願いします。

Mist

Re:fstreamの効率のいい使い方

#2

投稿記事 by Mist » 16年前

preタグの中に文章を書く場合は自動改行されませんので適宜改行してください。
「少々長くなりましたが、~」の行の後半が読めなくて質問内容がわかりません。

AC

fstreamの効率のいい使い方

#3

投稿記事 by AC » 16年前

あれ?
私の環境ではブラウザを小さくしても普通に見られるのですが。。。
preも閉じたはずです。

一応最後のところだけ再掲しておきます(適宜改行も入れておいたのでこれで大丈夫かな)。

(再掲)
少々長くなりましたが、テキストを任意の位置に追加(挿入)するようなプログラムを書いたつもりなのですが、
debugと書いたcoutによる出力では、ちゃんとできていることが確認できるのですが、
最後のfsによる書き込みが正常に出来ていないようです。
どこに問題があるのでしょうか?

それと、途中に挿入するような場合はわざわざこんな風にしないといけないのでしょうか?
もっと簡単にはできないのでしょうか(std::ios::appだと最後にしか書き込めないので…)?
効率のいい使い方等ありましたら、よろしくお願いします。
(ここまで)

ではよろしくお願いいたします。

Mist

Re:fstreamの効率のいい使い方

#4

投稿記事 by Mist » 16年前

書き込めない原因は調査中です。

挿入については以下でいいと思います。
// 全部読みこんで
	fs >> str1;

         // 挿入
	str1.insert(6, _T("test"));

GPGA

Re:fstreamの効率のいい使い方

#5

投稿記事 by GPGA » 16年前

質問内容とは別件ですが、tellgが返す値が文字数と同一の場合というのは
バイナリモードで開いたときのみで、テキストモード開いたときの値は環境依存になったはずです。

何をもって効率が良いと言うかによりますが
メモリに余裕があるのであれば、私なら

1:バイナリモードでファイルを開いてメモリに一括で読み込む
2:読み込んだデータをinsert_posまで書き込む
3:txtを書き込む
4:読み込んだデータをinsert_posから終端まで書き込む
5:ファイルを閉じる

とします。改行を1文字と考えないならば、2の前にinsert_posの値を修正します。

GPGA

Re:fstreamの効率のいい使い方

#6

投稿記事 by GPGA » 16年前

>>Mistさん
>fs >> str1;
ifstreamのoperator>>は改行があると、そこで読み込みを終了するので
この一文で全てを読み込むことはできません。

Mist

Re:fstreamの効率のいい使い方

#7

投稿記事 by Mist » 16年前

すいません、コメントが悪かったですね。
方法説明ということでテキスト内の一行が挿入位置よりも長いという前提で簡易に書きました。

AC

Re:fstreamの効率のいい使い方

#8

投稿記事 by AC » 16年前

GPGAさん、Mistさんレスありがとうございます。

>1:バイナリモードでファイルを開いてメモリに一括で読み込む
>2:読み込んだデータをinsert_posまで書き込む
>3:txtを書き込む
>4:読み込んだデータをinsert_posから終端まで書き込む
>5:ファイルを閉じる
ですが、1の手順の良い方法が解らないのです。
getline()で読みこむのでしょうか?
でもgetlineの第一引数にstd::string(例のプログラムの場合_tstring)が使えれば一番なのですが、どうも使えないようなので
(試しにやってみたらwchar_t*に変換できないといわれたので)、
データサイズを取得して、その分をmallocでTCHAR*で確保して…という風にするしかなさそうなのですが、
もっと簡単にやる方法とかもあるのでしょうかね。

とりあえず、もうちょっと色々調べて検討してみます。

それと、
fs << str1 << txt << str2 << std::endl;
がうまく動作してない原因のわかる方がいましたら、そっちの方もよろしくお願いします。

AC

Re:fstreamの効率のいい使い方

#9

投稿記事 by AC » 16年前

連投すいません、ちょっとだけ補足です。

>質問内容とは別件ですが、tellgが返す値が文字数と同一の場合というのは
>バイナリモードで開いたときのみで、テキストモード開いたときの値は環境依存になったはずです。
についてですが、最初バイナリモードで開いて色々やっていて、途中で普通のテキストモードにしたら、エラーが出て
というようないきさつがありました。

ここら辺についてももうちょっと調べてみます。

何かありましたら回答のほどよろしくお願いします。

Mist

Re:fstreamの効率のいい使い方

#10

投稿記事 by Mist » 16年前

str1 += txt + str2;

事前に結合してから出力するか

fs << str1;
fs << txt;
fs << str2;

分けて出力すれば動作しますね。

GPGA

Re:fstreamの効率のいい使い方

#11

投稿記事 by GPGA » 16年前

>ですが、1の手順の良い方法が解らないのです。

以下、一例です。
void InsertText(_tstring dir, DWORD pos, _tstring txt) {
	std::ifstream ifs(dir.c_str(), std::ios::in | std::ios::binary);
	ifs.seekg(0, std::ios::end);
	std::vector<char> buf(ifs.tellg());
	ifs.seekg(0, std::ios::beg);
	ifs.read(&buf[0], buf.size());
	ifs.close();

	std::ofstream ofs(dir.c_str(), std::ios::out | std::ios::binary);
#ifdef _UNICODE
	pos *= 2;
	const unsigned long len = txt.length() * 2;
#else
	const unsigned long = txt.length();
#endif

	ofs.write(&buf[0], pos);
	ofs.write((char*)txt.c_str(), len);
	ofs.write(&buf[pos], buf.size() - pos);
}
 

GPGA

Re:fstreamの効率のいい使い方

#12

投稿記事 by GPGA » 16年前

書き込み時にUNICODE対応してませんが、そこは流してください。

AC

Re:fstreamの効率のいい使い方

#13

投稿記事 by AC » 16年前

みなさん回答再びありがとうございます。

>事前に結合してから出力するか
>分けて出力すれば動作しますね。

とのことですが、自分の環境では結局変わりませんでした(環境を書き忘れてましたがVistaでVC++2008EEです)。
とりあえず、ifstreamとofstreamに分ければ正常に読み書きできるのでそうしておきます。

>書き込み時にUNICODE対応してませんが、そこは流してください。
GPGAさんのソースを見ながら、かなりいろいろいじくってみましたが、結局のところtcharとの共存は
やめた方がいいっぽいですね…。実装があまりにめんどくさすぎるので。
それと、unicode対応させようとしたのですが、テキストファイルの文章がマルチバイトだと、
文字数取得が難しく無理でした。
(ifs.seekg(0, std::ios::end);でファイルサイズを取得できますが、
それが文字数と全然関係ないため最後にヌル文字が大量にでてしまったりしました。
また、文字列取得を ifs >> で取得する手も考えましたが、全角スペースとかがあると
そこで一旦読み込みが止まるので、最終的に改行になるなどして上手くいきませんでした。)


ということで、大体納得しましたので一応解決とさせていただきます。
(それにしてもファイルの途中への文字の追記というのはあまりしないのですかね。追記が最後尾しかできないというのは変なように思えるのですが…。

閉鎖

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