Win2000/VC6を使っています。
CEditViewの改行コードはCRLFのようですが、
読み込み時に改行コードを判断して、CRとかLF
の改行コードのファイルをオープンしたいのですが
どんな方法がありますか?
CEditViewのどこをいじればいいのですか?
> 読み込み時に改行コードを判断して、CRとかLF
> の改行コードのファイルをオープンしたいのですが
> どんな方法がありますか?
どんなファイルだろうが、適切に判断して否応なくCRLFに置き換えてしまえばいい。
お返事ありがとうございます。
例えば、1MBのファイルをオープンするときに
FOR文で1024000回ループさせて、置換していたら
相当処理が遅くなります。
もし、CEditViewのメソッドを直接さわれるなら
きっと処理は速くなる気がします。
知恵を頂けないでしょうか?
CEditViewに改行コードを指定するような機能はなかったと思います。
例えそういう機能があったにせよ、
> 1MBのファイルをオープンするときに
> FOR文で1024000回ループさせて、置換していたら
> 相当処理が遅くなります。
置換処理をViewクラスの機能として実装するか、あなたがファイル読み込み時の処理とし
て実装するかの違いだけで、やっている事は結局変わりないように見えます。パフォーマ
ンス的にも。
改行コードの変換をしない場合と、前述の方法で改行コードを変換する場合とで、あなた
の環境でどの位パフォーマンスに差が出ますか? その差を何パーセント縮める事が要求さ
れていますか?
説明不足で申し訳ありません。
CRLFは2バイトでCRやLFは1バイトなので、
例えば、CRで改行しているファイルの中身をメモリに
読み込んで、CRLFに置換した場合、改行コードごとに
1バイトずつ、ずらしていかなければなりません。
この負荷が大きいように思われます。
パフォーマンス的には1MBのファイルをCEditView標準で
オープンした場合は1秒かかりませんが、改行コードを
置換した場合は、2~3分かかります。
1秒以内でオープンできたらいいなと思います。
以下は置換するロジックです。
workとwork2はTCHAR配列でworkにはファイルから読み込んだ
データが入っていて、work2は0x00で初期化されています。
BOOL flg = FALSE;
int idx2 = 0;
int max = _tcslen(work);
for(int idx=0;idx<max;idx++){
if(work[idx] == '\r'){
flg = TRUE;
}
if(flg && work[idx] != '\n'){
_tcsncat(work2,work,idx-idx2-1);
_tcscat(work2,\n);
idx2 = idx;
flg = FALSE;
}
}
if(idx2 == 0){
_tcscpy(work2,work);
}
恐縮ですが、よろしくお願いいたします。
# あげあしとりなことですが・・
>例えば、1MBのファイルをオープンするときに
>FOR文で1024000回ループさせて、置換していたら
1MB = 1024KB = 1024 * 1024 Bytes = 1,048,576 Bytesですから、
forのループは、1048576回まわさなければいけません。
> パフォーマンス的には1MBのファイルをCEditView標準で
> オープンした場合は1秒かかりませんが、改行コードを
> 置換した場合は、2~3分かかります。
とのことですが、
> _tcsncat(work2,work,idx-idx2-1);
> _tcscat(work2,\n);
このあたりの関数コールのオーバヘッドなどが大きいのでは?
1バイトずつの代入で実行してみては?
または、memcpy()を使ってみるとか・・・
> _tcsncat(work2,work,idx-idx2-1);
> _tcscat(work2,\n);
これが、毎回work2の先頭から文字列終端を探しますから、
無意味に時間が掛かっているのでしょうね。
char* p2 = work2;
for (char* p = work; *p != '\0'; p++) {
if (*p == '\r') {
*p2++ = '\r';
*p2++ = '\n';
} else if (*p == '\n') {
// ignore
} else {
*p2++ = *p;
}
}
*p2 = '\0';
というか、ほんとに遅いと思うのであれば、プロファイルをとってみては?
VC6ならできるはずです。
私も_tcsncatや_tcscatが足を引っ張っていると思います。
自分で位置を管理してやった方が早いでしょう。
CEditView::OnReplaceAll()や、CString::Replace()が使えると数行ですみそうですが、
おそらく中で同じようなことをやっているので、処理時間はあまり変わらないみたいで
す。単純にループでまわしたほうが早いみたいです。dairygoodsさんと重複してしまう
かも知れませんが、CDocクラスのSerialize()を下の様に変更してみてください。1MB近
くのファイルでもあまり違和感なく開けると思います。
// 改行コードが'\r'のみのファイルを開く処理です。
void CXXXDoc::Serialize(CArchive& ar)
{
//((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
// デフォルトの一行はコメントアウト
DWORD size;
char* work1;
char* work2;
char* p;
UINT i, j, n;
if(ar.IsLoading()){ // 読み込み処理
// ファイルの中身を直接バッファにコピー
size = ar.GetFile()->GetLength();
work1 = new char[size / sizeof(char)];
ar.GetFile()->ReadHuge(work1, size);
// '\r'の個数計算
n = 0;
p = strchr(work1, '\r');
while(p != NULL){
n ++;
p = strchr(p + 1, '\r');
}
// ファイルのサイズと、'\n'を挿入する分のメモリを確保
// +1は'\0'の分
work2 = new char[size / sizeof(char) + n + 1];
j = 0;
for(i = 0; i < size; i ++){
if(work1[i] == '\r'){
work2[i+j] = '\r';
work2[i+j+1] = '\n';
j ++; // '\n'を挿入した分 j だけずらす
}
else
work2[i+j] = work1[i]; // そのままコピー
}
work2[size + n] = '\0';
((CEditView*)m_viewList.GetHead())->GetEditCtrl().SetWindowText(work2);
delete[] work1;
delete[] work2;
}
else if(ar.IsStoring()){ // 書き込み処理
// デフォルトの書き込み処理、もし改行処理を反映したくない
// ようでしたら、読み込みの逆をしてください。
((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
}
}
一気に読んで、ほんでもってズラすから遅いんちゃうかな。
一個づつ読んだほうが速いように思える
while ( 文字一個読めるなら読む ) {
読んだ文字を書く
それがCRだったらLFも書く
}
おしまい。
επιστημηさん:
> どんなファイルだろうが、適切に判断して否応なくCRLFに置き換えてしまえばいい。
ということで、
while( 文字一個読めるなら読む )
{
読んだ文字を書く
if( それがCRだった && 次の文字はLFじゃない )
{
LFも書く
}
}
ではどうでしょう?
私が前にやったのは、ワークAとBを用意して、
ワークAにファイルの中身を丸ごと格納。
あとは改行コードが来るまでBにコピー。
コード来たらBにコード書き込み。
これが一番速くないですか?
最初CStringのReplace使ってやってたんですけど、
激遅でした。30万行ぐらいのデータやるのに
えらく時間がかかった記憶があります。
普通にポインタでやったら、1~2秒で出来ましたよ。
あまり深く考えない方が良いかと。
改行コードの混じったテキストファイルを
VCにドラッグ&ドロップすると、勝手に変換してくれますが
えらく時間がかかる。
あれは何やってんだろう
皆さん、いろんな意見を頂いてありがとうございました。
_tcsncatで終了文字を探すのに時間がかかっているようですね
dairygoodsさんのロジックをそのまま使わさせて頂きき、
一気に解決しました。
1秒くらいになりました。
人のソースを見ると本当に勉強になります。
ありがとうございました。