CStdioFileについて

[戻る]
いけやん 2008/07/31(木) 09:05:50
ども、最近毎日質問している

いけやんです。

CStdioFileの事でエラーが出て困っています。

開発環境
WinXP Pro
Visual C++6.0
MFC ダイアログベース

CTreeCtrlで表示しているノードを

テキストに格納して、ファイルオープン等で、読み込んで

ツリービューに表示したいのですが

VC++の参考書ではfopen系の機能をまとめた

「CStdioFile」を使うと書いてあるのですが、

リファレンスと参考書を見比べるていて、

引数や使い方が違うので困っています。

また、自分なりに書いたソースでは

「無題のファイルには無効のパスが含まれています。」

というエラーが出ます。

ネット等で検索すると他の方もなってたみたいで

その方は、fopenを使ったらエラーが出なくなったそうです。

「CStdioFile」の使い方をご存知の方、教えて頂けないでしょうか?

よろしくお願いします。

いけやん 2008/07/31(木) 09:07:22
これがソースです。

CString  str[75],fname;  
CFile    File;
int i;
    CStdioFile fin(fname,CFile::modeRead);

    if ( !File.Open( _T("C:\\vctree\\*.txt"), CFile::modeRead, NULL ) )
    {
        AfxMessageBox("ファイルオープンエラーで       
す。",MB_OK|MB_ICONINFORMATION);
    }
    while(fin.ReadString(str[i]) != NULL){

    m_HndTree[0] = m_tree.InsertItem("str[0]",TVI_ROOT);
    
    m_HndTree[1] = m_tree.InsertItem("str[1]",m_HndTree[0]);
    m_HndTree[2] = m_tree.InsertItem("str[2]",m_HndTree[1]);
    m_HndTree[3] = m_tree.InsertItem("str[3]",m_HndTree[1]);
    省略
        }

Blue 2008/07/31(木) 09:09:15
> 自分なりに書いたソースでは
は具体的にどのようなコードなのでしょうか?

> リファレンスと参考書を見比べるていて
参考書はバージョンがいくつのものについて記述されていますか?
リファレンスはバージョンがいくつのものについて記述されていますか?

Blue 2008/07/31(木) 09:16:21
>if ( !File.Open( _T("C:\\vctree\\*.txt"), CFile::modeRead, NULL ) )
は何のためでしょうか?
Windowsでは*.txtのようなファイルは存在することはできません。

ファイルが開けたかどうかはCStdioFileのコンストラクタでエラーをthrowするので
それをcatchすることで確認できます。

例)

CString strFileName(_T("a.txt"));

TRY
{
    CString strLine;

    CStdioFile fInStream(strFileName, CFile::modeRead | CFile::typeText);
    while (fInStream.ReadString(strLine))
    {
        AfxMessageBox(strLine);
    }
    fInStream.Close();
}
CATCH (CFileException, e)
{
    // エラー処理
}
END_CATCH

いけやん 2008/07/31(木) 09:18:04
早速の返信ありがとうございます。

すいません。記述するのを忘れていました。

http://msdn.microsoft.com/ja-jp/library/6337eske.aspx
リファレンスはこれを参考にしています。

参考書は,6.0「新Visual C++6.0入門」を参考にしています。

になります。お願いします。

いけやん 2008/07/31(木) 09:28:33
if ( !File.Open( _T("C:\\vctree\\*.txt"), CFile::modeRead, NULL ) )

↑の部分は、恥ずかしながら

openエラーのつもりです。

「*.txt」の部分は、最初「部材.txt」にしていたのですが

一緒のエラーがでるので

コマンドプロンプトの命令で、*::ワイルドカードでしたっけ?

「*.txt」この様な感じで使えるかな〜と思い書いていました。

ITO 2008/07/31(木) 11:15:40
>    CStdioFile fin(fname,CFile::modeRead);
fnameが空でないですか?
実際に使うファイル名を代入しないとだめです。

> コマンドプロンプトの命令で、*::ワイルドカードでしたっけ?
> 「*.txt」この様な感じで使えるかな〜と思い書いていました。
CFileDialogでファイル名を取得します。
詳しくはMSDNで調べてください。
参考書にも載っていると思います。

いけやん 2008/07/31(木) 15:07:45
すいません。色々やっていて、返信遅くなりました。

Blueさんすいません。

Blueさんの例、ほとんど丸々使わさせて頂いてます。

Blueさん,ITOさんのおかげで、エラーは取れました。

が、まだ少し質問が残っています。

@Seekの使い方について

Aテキストで読み込んだ文字列をどうやって親子関係にすればいいのか

の順に質問したいと思います。

まず@なんですが現在テキストの中は

1000|01-00-00|《aaaaa》 こういう感じになっています。

そこでSeekを使って「《aaaaa》」これだけを取り出したいのですが
使い方が違うのか、1列丸ごと表示されます。

  CString strLine[15];

  CStdioFile fInStream(strFileName, CFile::modeRead | CFile::typeText);
  fInStream.Seek( 5, CFile::begin );
  while (fInStream.ReadString(strLine[i]))
  {
    //AfxMessageBox(strLine);
    m_HndTree[0] = m_tree.InsertItem(strLine[i],TVI_ROOT);
    省略
  }

よろしく、お願いします。

επιστημη [HomePage] 2008/07/31(木) 16:31:19
> fInStream.Seek( 5, CFile::begin );
読み出し位置がちょっとズレるだけですけど...

とにかく一行まるっと読んで、欲しいトコだけ抽出するが吉。

いけやん 2008/07/31(木) 16:58:39
すいません。その処理が解りません。

その摘出する方法を教えて頂けないでしょうか?

επιστημη [HomePage] 2008/07/31(木) 17:48:14
'|'を区切り文字としてstrtokとかSplitとか使えばいいんじゃないかと。

いけやん 2008/07/31(木) 17:58:03
επιστημηさんの

おっしゃった、strtok,Split調べてやってみます。

あちこちに、ぶつかりながらなので

返信が遅くなるか、明日になるかもしれません。

ITO 2008/07/31(木) 18:43:54
CStringですよね。

>1000|01-00-00|
ここまでも文字数は固定ですか?
固定なら
   CString slに一行分のデータを読み込んで
    CString s = sl.Mid(文字数+1);
とすれば、sに「《aaaaa》」のデータが入るはずです。
詳しくはMSDNで調べてください。
CStringを調べてみてください。

ITO 2008/07/31(木) 19:03:07
修正です。
>CString s = sl.Mid(文字数+1);
     ↓
CString s = sl.Mid(文字数);
こうですね。
詳しくはMSDNで調べてください。

アキラ [HomePage] 2008/07/31(木) 19:20:51
> Blueさん

> ファイルが開けたかどうかはCStdioFileのコンストラクタでエラーをthrowするので
それをcatchすることで確認できます。

例外処理をエラー処理として使用しちゃいけないでしょう。(C++では)
なので、例外処理よりは Open の戻り値でエラーを拾うのが正しいです。

Blue 2008/08/01(金) 00:40:42
>例外処理をエラー処理として使用しちゃいけないでしょう。(C++では)
となると、try〜catchって使わないってこと?

それと、ファイルオープンだけ例外が出るわけではないので、全体をtry〜catchで囲った
わけなんですが、これも問題ありなんでしょうか?

Blue 2008/08/01(金) 00:43:13
ちなみに
>// エラー処理
と書いたのはエラー処理を行うというわけではなく、
エラーのときに行うときの処理を記述するという意味合いで書きました。
(たとえばメッセージボックスを出したり。)

アキラ [HomePage] 2008/08/01(金) 01:12:55
try/catch はできる限り使わない、ということです。
ファイルオープンのエラー処理は if 文で解決できますよね。

例外処理は、 C++ では if 文に比べ数百倍遅くなるので
if 文で解決できるようなエラー処理に例外処理を使用してはいけません。
例外でしかエラーメッセージ取れない場合があったらしょうがないかもしれませんけど。
(.NET や Java なら例外処理は速いのでエラー処理として使用してもかまいません)

アキラ [HomePage] 2008/08/01(金) 01:22:39
try/catch を極力使わないようにすれば、全体を try/catch で囲む
ということも少なくなり、より局所的になるでしょう。
このトピックで使われてる CStdioFile のメンバ関数で例外を投げるのは
コンストラクタと Seek くらいですしね。

Ban 2008/08/01(金) 07:51:34
基本方針という意味では概ね同意で、理解もしますが、
いけません、とか正しくない、はいいすぎな気がしますけど。

例えばこの部分で100ms遅くなったとしても、
それがどれほど致命的な箇所なのか?という話があって、
可読性が確保できるなら個人的にはこの程度はアリかなぁと。

これが組み込み開発やってて起動速度(やサイズ)の向上が最優先とか、
ここ自体が実行中にユーザの体感速度に直結するとかならわかるのですが、
どうもそうと断言するほどの情報は無いように思います。

C言語ライクに例外使わないなら使わないで統一ってのはまだいいとして、
「極めて局所的にtry/catchしてる」ソースって、すごく読みにくいのが
多いと個人的に思ってます。(大抵、外部モジュール都合?)
関数単位くらいでのtry/catchは可読性として悪くないと思いますが。

# 例外可能性をちゃんと考えずに書かれたコードがちょっと混じるだけで、
# すぐbad_allocの可能性とか出てきて結局全域的にtry/catchせざるを
# 得ないことも少なくないのが現実な気も…>PCアプリ。

Blue 2008/08/01(金) 08:41:58
>コンストラクタと Seek くらいですしね。
ReadStringやデストラクタでも例外返しませんか?

かもねぎ 2008/08/01(金) 08:49:02
C++の例外処理機能ではなくてSEH使うほうが良いのですかね?

tetrapod 2008/08/01(金) 09:01:07
デストラクタで例外は勘弁して欲しい・・・そんなライブラリが合ったら即捨てだ
CStdioFile のデストラクタで例外は投げられないはずだ

SEH を使うなら SEH でのみ統一
C++ 例外を使うなら C++ 例外でのみ統一、だな
MFC は C++ 例外を投げるはず。C++ 例外機構で受け取るほうがいい

アキラ [HomePage] 2008/08/01(金) 09:23:11
たしかに、「いけません」は言いすぎでした。
ですが、例外にかかるコストというのはよく考えなければならないことです。
例外を使用しなくてすむのであればそうしたほうがいいでしょう。

> 例えばこの部分で100ms遅くなったとしても
これも、コストをよく理解したうえで使用するならかまわないでしょうが
「最近のPCは早いから〜」で作っていたらたちまちボトルネックになってしまうでしょ
う。

> # 例外可能性をちゃんと考えずに書かれたコードがちょっと混じるだけで、
> # すぐbad_allocの可能性とか出てきて結局全域的にtry/catchせざるを
どの関数が例外を投げる/投げないを調べたうえで使えばこれは問題にならないでしょ
う。
「例外を投げない」という強い保証がある関数にはそもそも例外可能性なんてものはない
ですから。

> ReadStringやデストラクタでも例外返しませんか?
投げませんよ。
http://msdn.microsoft.com/ja-jp/library/x5t0zfyf(VS.80).aspx

また、「デストラクタでは例外を投げてはいけない」という原則があります。

ITO 2008/08/01(金) 09:59:26
> try/catch はできる限り使わない、ということです。
> ファイルオープンのエラー処理は if 文で解決できますよね。
MFCのCFile系のクラスはC++のtry/catchに対応しているのでtry/catch
の文は使っています。
結果は利用してませんが..............
> try/catch を極力使わないようにすれば、全体を try/catch で囲む
> ということも少なくなり、より局所的になるでしょう。
 MFCがtry/catchに対応している以上、try/catch文のルーチンは作ったほうが
いいと思います。
 エラーは戻り値で判断すればいいだけですよね。
>> ReadStringやデストラクタでも例外返しませんか?
>投げませんよ。
CStdioFileはOpenとCStdioFile(構築)だけですね。

ITO 2008/08/01(金) 10:17:49
>CStdioFileはOpenとCStdioFile(構築)だけですね。
CStdioFileはOpenとCStdioFile(構築)だけではないみたいです。
CStdioFileのほかのクラスメンバーはないみたいです。

いけやん 2008/08/01(金) 10:19:52
すいません。返信遅れました。

みなさん難しい話をされていますね〜。

私には、ちょっと解らないですね。水を差す様で悪いんですが

ITOさんのMid関数使ってうまくいきました。ありがとうございます。

str[i] = strLine[i].Mid(14);使い方が簡単で助かりました。

すいません。少し戻るのですが、Seekの使い方について

今現在テキストの中は

----+--------+------------------------------------
1000|01-00-00|《aaaaa》          

このようになってます。テキストが長いので区切りのために線等いれてます。

それを無理やりfInStream.Seek( 54, CFile::begin );

----+--------+------------------------------------の部分を飛ばしています。

聞きたいのは、こんな使い方でいいのでしょうか?

後、第一、第二、引数とかが知りたいです。

もしかしてmsdnに載っていますか?お願いします。

επιστημη [HomePage] 2008/08/01(金) 10:32:06
> こんな使い方でいいのでしょうか?

好ましくありません。'-'が一個でも増減したら即アウト。
一行まるっと読んで「'-'から始まってたら無視」の方が100倍マシ。

いけやん 2008/08/01(金) 14:47:23
すいません。返信遅くなりました。

strtokを使ってテキストの"-","+","|"

飛ばしたいのですが、引数を調べるとchar *では、

ないといけないという事が分かったのですが

今CString型で文字列を取っているので、第一引数を

どうすればいいのか解らないです。

教えて頂けないでしょうか。お願いします。

char *ss;

while (fInStream.ReadString(strLine[i]))
{
    ss = strLine[i];
    strLine[i] = strtok(ss,"-");
    strLine[i] = strtok(ss,"+");
    strLine[i] = strtok(ss,"|");
    strLine[i] = strtok(ss," ");
    //str[i] = strLine[i].Mid(14);
            
    m_HndTree[0] = m_tree.InsertItem(str[i],TVI_ROOT);
}

επιστημη [HomePage] 2008/08/01(金) 15:12:35
#include <afx.h>
#include <string.h>
#include <iostream>

using namespace std;

int main() {
  CStdioFile fin;
  if ( !fin.Open("test.txt", CFile::modeRead, NULL ) ) 
    return -1;
  CString line;
  while ( fin.ReadString(line) != NULL ) {
    int i = 0;
    for ( const char* token = strtok(line.GetBuffer(line.GetLength()),"+-|");
          token != NULL; 
          token = strtok(0,"+-|") ) {
      cout << i++ << "[" << token << "] ";
    }
    line.ReleaseBuffer(-1);
    cout << endl;
  }
}

-- test.txt --
1000|01-02-03|zero
2000|02-03-04|one
3000|03-04-05|two
4000|04-05-06|three
5000|05-06-07|four
6000|06-07-08|zero

-- 実行結果 --
0[1000] 1[01] 2[02] 3[03] 4[zero]
0[2000] 1[02] 2[03] 3[04] 4[one]
0[3000] 1[03] 2[04] 3[05] 4[two]
0[4000] 1[04] 2[05] 3[06] 4[three]
0[5000] 1[05] 2[06] 3[07] 4[four]
0[6000] 1[06] 2[07] 3[08] 4[zero]

いけやん 2008/08/01(金) 20:16:32
επιστημηさん、サンプルありがとうございます。

そのまま使わせて貰ってます。

//cout << i++ << "[" << token << "] ";←ここでエラー
    }
    str.ReleaseBuffer(-1);
    //fin.Close();
    cout << endl;
  }

エラーとりをしていて返信遅くなりました。

正直、cout <<こういう物を見た事がなくてエラーが出た時あせりました。

Printf?みたいな物なんですかね・・・

すいません。また、新たに質問したい事が出来ました。

1000|01-01-01|aaaaaとかで"|"や"-"等は飛ばせる様になったのですが

現在テキストの中に<<aaa/aaa>>こういった"/"が含まれています。

現在プログラムでは、"+-|"だけ飛ばす様にしているのですが

なぜか、"/"も飛ばされています。

すいませんが、教えて貰えないでしょうか。
お願いします。

ソースです。

for ( const char* token = strtok(str.GetBuffer(str.GetLength()),"+-|");
    token != NULL; 
    token = strtok(0,"+-|") ) {

    //token = str.Mid(14);

    m_HndTree[0] = m_tree.InsertItem(token,TVI_ROOT);

επιστημη [HomePage] 2008/08/01(金) 21:38:22
InsertItemする直前のtokenがどうなってるくらいは確認したんでしょうね?

Ban 2008/08/02(土) 19:43:27
> どの関数が例外を投げる/投げないを調べたうえで使えばこれは問題にならないでしょう。
> 「例外を投げない」という強い保証がある関数にはそもそも例外可能性なんてものはないですか
ら。

参照先MSDNを見ても、「例外を投げない強い保証」がないようです。
(私の理解では、「例外を投げない強い保証」とは、「投げない」または「throw()」の明記です)


>> ReadStringやデストラクタでも例外返しませんか?
> 投げませんよ。
> http://msdn.microsoft.com/ja-jp/library/x5t0zfyf(VS.80).aspx

上記で「投げない」と書いてない以上、投げる可能性があると考えるべきでしょう。
# VC6バンドルのMSDNに明記があるかは未確認です。

実際、手元のVC7.1(質問者環境のVC6は今手元にないので)のソースでも、
CStdioFile::ReadString(CString&)には、明確にAfxThrowFileExceptionを呼ぶパスがありま
す。
# AfxThrowFileExceptionはCStdioFile::ReadString(LPTSTR,UNIT)の方にもあります。

また、内部で呼んでいるCString::GetBufferでもMemoryExceptionを投げる可能性があるはず。


> また、「デストラクタでは例外を投げてはいけない」という原則があります。

これは、明記がなくても信じてもいい/信じたいところです。

Ban 2008/08/02(土) 20:03:47
正しく実装していれば実際の投入可能性がないかもしれませんが、
CStdioFile::ReadString(LPTSTR,UNIT)の方には、
AfxThrowInvalidArgException()の呼び出しもありますね。


CStdioFile::ReadString(LPTSTR,UNIT)のソースを読む限り、
> lpszResult == NULL && !feof(m_pStream)
という条件判断で例外を投げてますので、
実装上の挙動としては、こんな感じ(↓)でしょうか。

> データを読み込まずにファイルの終わりに達した場合は NULL または
> FALSE (ブール型の場合) を返します。
「ファイルの終わりに達せずにデータが読み込めなかった場合は
CFileException例外を投げます」

そして、おそらく、公式の仕様見解はこう?(↓)
「ファイルの終わりに達せずにデータが読み込めなかった場合の動作は未規定です」

Ban 2008/08/02(土) 20:25:09
# 散発的にすみません。

本題に立ち返ると、下記の前提認識にずれがあり、MSDNをこの理解で読むのは
誤りという点がまずあって、
> このトピックで使われてる CStdioFile のメンバ関数で例外を投げるのは
> コンストラクタと Seek くらいですしね。

その上で、「各関数に例外投入可能性がある」という認識にたってみれば、
個別に「極めて局所的にtry/catch」は著しく読みにくくありませんか、という話です。

MFC側で例外を投げてくる可能性があるものを使う以上、
PCの性能云々は関係なく、どこかでcatchはせざるを得ないはずです。
(「そこで例外がでたら落ちる仕様」という割り切りがない限りは)

> 「最近のPCは早いから〜」で作っていたらたちまちボトルネックになってしまうでしょう。

むしろ、MFCなどMSの設計思想がそもそもこれに近いように思います。

かもねぎ 2008/08/02(土) 20:58:16
別スレ立てますか?(例外関係)

ITO 2008/08/02(土) 21:35:20
> 別スレ立てますか?(例外関係)
雑談ラウンジに別スレ立てました。
宜しく

いけやん 2008/08/04(月) 10:32:49
返信遅れました。申し訳ないです。

>  InsertItemする直前のtokenがどうなってるくらいは確認したんでしょうね?

for ( const char* token = strtok(str.GetBuffer(str.GetLength()),"+-|0123456789 ");
      token != NULL; 
      token = strtok(0,"+-|0123456789 ") ) {

      AfxMessageBox(token);//確認


      m_HndTree[0] = m_tree.InsertItem(token,TVI_ROOT);
      省略
}

こんな方法しか、思かびませんでした。

これでは、確認になっていませんか?

PATIO 2008/08/04(月) 11:33:12
せっかくVC++6.0を使っているんですから
デバッガを使ったほうが早くないですか?
デバッガならステップ毎に状況の確認も出来ますし。

あと、VC++の入門書よりもC++言語の方を先に読んだ方が良く無いかなぁと
感じました。cout <<を見た時に分からない様子でしたので。
VC++の使い方を勉強するのも必要だとは思うんですが、
コンソールアプリでC++言語の勉強をしてからMFCの使い方を勉強した方が
すんなり理解出来る所が多いのではないかなと思いました。

いけやん 2008/08/04(月) 11:42:34
PATIOさん、アドバイスありがとうございます。

基本的な事なら、すいません。
C++の参考書でも、VC++の勉強になるのですか?

ふと今思ったのですが、[cout <<]←ってC++,VC++で使えるのですか?

いけやん 2008/08/04(月) 14:54:06
> なぜか、"/"も飛ばされています。
上記は間違いですね。分解されて表示されていました。

ブレイクを張ってtokenの中を見たのですが

●for(const char* token = 
            strtok(str.GetBuffer(str.GetLength()),"+-|0123456789 ");@("aaa/aaa")
    token != NULL; 
●  token = strtok(0,"+-|0123456789 ") ) {A("aaa"),("/aaa")

@では"aaa/aaa"でそのまま読み込まれているのですが、

Aでは"aaa"と"/aaa"に分解されていました。

なぜ分解して表示されるのか、解りません。

よろしくお願い致します。

επιστημη [HomePage] 2008/08/04(月) 15:19:24
...再現できないのでわかりません。

#include <iostream>
#include <cstring>

using namespace std;

int main() {
  char data[] = "ab/cd|ef/gh/ij|kl/mn/op";
  for ( const char* token = strtok(data,"+-|0123456789");
        token != NULL; token = strtok(0,"+-|0123456789") ) {
    cout << '[' << token << "]\n";
  }
}

たいちう 2008/08/04(月) 15:26:50
> C++の参考書でも、VC++の勉強になるのですか?

「ランニングをして、野球の練習になるのですか?」
基礎体力を付けるのには、野球の練習よりもランニングでしょう。

いけやん 2008/08/04(月) 16:00:23
> 基礎体力を付けるのには、野球の練習よりもランニングでしょう。
ごもっともですね。

επιστημηさんのサンプルでは、"/"で分解されず

[ab/cd|ef/gh/ij|kl/mn/op]とちゃんと表示されました。

ソース乗せます。

//--------------------------------------------------------------------
CFile file;

CString str;//1行文読込用
CStdioFile fin;

if ( !fin.Open("部材.txt", CFile::modeRead, NULL ) ) {return -1;}

while ( fin.ReadString(str) != NULL ) {
int i = 0;
    
for (const char* token=strtok(str.GetBuffer(str.GetLength()),"+-|0123456789 ");
     token != NULL; 
     token = strtok(0,"+-|0123456789 ") ) {

    m_HndTree[0] = m_tree.InsertItem(token,TVI_ROOT);
    
    m_HndTree[1] = m_tree.InsertItem(token,m_HndTree[0]);
    m_HndTree[2] = m_tree.InsertItem(token,m_HndTree[1]);
    m_HndTree[3] = m_tree.InsertItem(token,m_HndTree[1]);

    m_HndTree[4] = m_tree.InsertItem(token,m_HndTree[0]);
    m_HndTree[5] = m_tree.InsertItem(token,m_HndTree[4]);
    m_HndTree[6] = m_tree.InsertItem(token,m_HndTree[4]);
    m_HndTree[7] = m_tree.InsertItem(token,m_HndTree[4]);

    m_HndTree[8] = m_tree.InsertItem(token,m_HndTree[0]);
    m_HndTree[9] = m_tree.InsertItem(token,m_HndTree[8]);
    m_HndTree[10] = m_tree.InsertItem(token,m_HndTree[8]);

    m_HndTree[11] = m_tree.InsertItem(token,m_HndTree[0]);
    m_HndTree[12] = m_tree.InsertItem(token,m_HndTree[11]);
    m_HndTree[13] = m_tree.InsertItem(token,m_HndTree[11]);
    m_HndTree[14] = m_tree.InsertItem(token,m_HndTree[11]);
      //cout << i++ << "[" << token << "] ";
    }
    str.ReleaseBuffer(-1);
    //fin.Close();
    cout << endl;
 }

いけやん 2008/08/04(月) 16:03:06
テキストの中
----+--------+------------------------------------
2000|02-00-00|《aaaaaa》                    
2000|02-01-00|    aaa/bbb                      
2000|02-01-01|    ccc                    
2000|02-01-02|    ddddd                      
2000|02-01-03|    ee            
//------------------------------------------------

実行結果
//------------------------------------------------          
<<aaaaaa>>
aaa
/aaa
ccc
ddddd
ee
//------------------------------------------------
になります。お願いします。

επιστημη [HomePage] 2008/08/04(月) 16:08:18
全角文字にstrtok使っちゃダメでしょぉ!
strtokは全角文字を想定して作られていません。
使ったあんたが悪い。

いけやん 2008/08/04(月) 16:29:14
すいません。上記では、全角になっているのですが

テキストの中はちゃんと半角になっていました。

すいません。記載ミスです。

επιστημη [HomePage] 2008/08/04(月) 16:47:16
...しょーじきわからん。ありえねぇ。
# てか再現できねぇ

いけやん 2008/08/04(月) 17:24:54
ん・・・?
> 全角文字にstrtok使っちゃダメでしょぉ!
> strtokは全角文字を想定して作られていません。

という事は、
全角の文字列を読み込まそうとするのに、strtokを使っては
ダメという事ですね?

ITO 2008/08/04(月) 20:49:25
> という事は、
> 全角の文字列を読み込まそうとするのに、strtokを使っては
> ダメという事ですね?
通常 ランタイムライブラリーは半角はだめです。
全角用の関数が用意されている場合がありますが、
使ったことないですね。

いけやん 2008/08/05(火) 09:44:27
> 通常 ランタイムライブラリーは半角はだめです。
C言語の標準なライブラリの事を示す。←ついさっき知りました。

> 通常 ランタイムライブラリーは半角はだめです。

すいません。
上記でちょっと混乱しているのですが、strtok関数はCRT(ランタイムライブラリ)
から外れるって事ですか?

> 全角文字にstrtok使っちゃダメでしょぉ!
 ↑これをみる限りstrtok関数は半角しか使ってはいけないって事ですよね?

> 通常 ランタイムライブラリーは半角はだめです。
 ↑全角しかダメって事ですよね?

「このふたつから、strtokはCRTではなく半角読込に使って
 CRTで用意されている全角読込関数を使う」
という解釈でいいんですか?

επιστημη [HomePage] 2008/08/05(火) 10:41:01
ここまできたらいいかげんマニュアルを読め。
http://msdn.microsoft.com/ja-jp/library/2c8d19sb(VS.80).aspx

ITO 2008/08/05(火) 10:44:18
>> 通常 ランタイムライブラリーは半角はだめです。
> ↑全角しかダメって事ですよね?
そうですね全角がだめです。

混乱させちゃったようですみません。

>「このふたつから、strtokはCRTではなく半角読込に使って
> CRTで用意されている全角読込関数を使う」
> という解釈でいいんですか?
いいと思います。

区切り文字は、出来る限り半角にしたほうがいいですね。

επιστημη [HomePage] 2008/08/05(火) 11:05:44
> 区切り文字は、出来る限り半角にしたほうがいいですね。

区切り文字は半角必須。そうじゃないと二つの区切り文字とみなされます。
分割対象の文字列も半角必須。全角1文字を半角2文字として扱うため、
場合によっては全角文字の一部が区切り文字と誤認識され、ぶった切られます。

いけやん 2008/08/05(火) 11:47:18
> ここまできたらいいかげんマニュアルを読め。

一番上に「ランタイム ライブラリ リファレンス」と書いてありました。
επιστημηさん、怒らしたならすいませんでした。

すいません。
また怒らすかもしれませんが
例えば、テキストの中が

---------------------------------
1000-01-01|<<aaaaa>>::"あああああ"
---------------------------------

という風になっていたなら、

1000-01-01|<<aaaaa>>::まではstrtok等の半角関数で読んで

"あああああ"は別の全角関数で読み込むという風に

処理を分けた方がいいという事ですか?

επιστημη [HomePage] 2008/08/05(火) 11:50:50
...やっぱり読んでない。
そのマニュアル・ページに"マルチバイト文字列対応版"も載ってたはずだが?

tetrapod 2008/08/05(火) 11:53:07
中氏の代わりに突っ込みいれていいっすか?全角ってなにっすか?

MBCS なんぞ捨てて wcstok なら問題ないぢゃんとか
マルチスレッドで strtok 正しく動くんだっけとか
そんな不安要素を抱えたもの使うくらいなら boost::tokenizer に汁とか
正規表現クラスのほうがもっと使いやすいんぢゃないかとか
CString のままでも SpanExcluding とかでなんとかなりそうとか

επιστημη [HomePage] 2008/08/05(火) 12:08:36
# 中さんがなんと言おうが全角は全角っしょ♪ 通じるからね。

CString::Tokenizeならもっと簡単とか。
そーゆーことも含めてマニュアル読め、と。

いけやん 2008/08/05(火) 12:48:02
調べ方が足りませんね。すいませんでした。
調べてまた出直してきます。

たいちう 2008/08/05(火) 12:51:07
> 1000-01-01|<<aaaaa>>::まではstrtok等の半角関数で読んで
> "あああああ"は別の全角関数で読み込むという風に
> 処理を分けた方がいいという事ですか?

全角が混じる可能性があるならば、全角に対応している関数を使おう、
ということです。処理を分ける必要はありません。

CString::Tokenize()かCString::Find()を使って、
自分で切り分けてはいかがでしょう。

tetrapod 2008/08/05(火) 13:30:04
VC++6 の MFC4.2 の CString には Tokenize がないんだよねー
だから SpanExcluding を例に挙げといたんだが

SpanExclude で抽出→Mid で既読部分を削除→TrimLeft で先頭セパレータを削除→
の繰り返しで Tokenize 相当のことは出来そうな気がする
っていうかやってみた(やっつけ:参考にしないでね)

struct cstring_tokenizer {
    CString str;
    const TCHAR* separators;
    cstring_tokenizer(const CString& str0, const TCHAR* sep0) : str(str0), 
separators(sep0) {}
    CString read_tokenized_string() { // destructive read
        CString tmp(str.SpanExcluding(separators));
        str=str.Mid(tmp.GetLength());
        str.TrimLeft(separators);
        return tmp;
    }
};

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) {
    AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0);
    CString s(_T("1000-01-01|<<aaaaa>>::表示能力"));
    cstring_tokenizer tk(s, _T("|<>:\\"));
    CString t;
    for (t=tk.read_tokenized_string(); t.GetLength()>0; 
t=tk.read_tokenized_string()) {
    std::cout << static_cast<const TCHAR*>(t) << std::endl;
    }
    return 0;
}

επιστημη [HomePage] 2008/08/05(火) 13:38:43
> VC++6 の MFC4.2 の CString には Tokenize がないんだよねー

そか、VC6かー... orz
もうね、いいかげんいつまでそんな古いの使ってんだと。

ITO 2008/08/05(火) 16:54:24
うーん、
すべて半角のデータで実行させたらstrtokでうまくいったのですか?

いけやん 2008/08/05(火) 17:08:09
半角どころか全角もうまく行ってました。

一部では、
> 全角1文字を半角2文字として扱うため、
> 場合によっては全角文字の一部が区切り文字と誤認識され、ぶった切られます。

これになりました。
ちなみにこの漢字は「掛」ぶった切られました。

PATIO 2008/08/05(火) 18:23:21
ワープレスですみません。

> 基本的な事なら、すいません。
> C++の参考書でも、VC++の勉強になるのですか?

VC++の本はC++言語の文法は理解していること前提で書いてある事が
殆どですので、その前提が崩れるとVC++の本を読んでも理解できない部分が
多々出てきます。
C++言語を知らないとMFCは使いこなせないです。
特にクラスの派生やら仮想関数周りは知っておく必要があると思います。
なので、MFCを使ったプログラミングをする前にC++の勉強をしておく方が
良いのでは無いかと言う提案です。
MFCとC++言語の勉強を同時にやるよりも先にC++言語の知識をきちんと
付けた方がMFCもすんなり理解できますし、お得だと思います。


> ふと今思ったのですが、[cout <<]←ってC++,VC++で使えるのですか?
使えるはずですよ。
但し、名前空間の話があるのでその辺まで理解した上で無いと
使えないとかなりそうですけれど。
VC++も結局はC++言語の上で成り立っている開発環境なので
その辺の標準的な部分は同じです。
但し、VC++6.0のコンパイラは最新のC++言語の標準からすると
準拠率が低く、お勧めできる物では有りません。
VC++6.0でないと駄目な理由が無い限りは最新の環境で勉強された方が
よろしいかと思います。
コンパイラもライブラリも最新の物の方が準拠率は高いです。

rin 2008/08/05(火) 20:44:24
VC++:開発用のソフトの名前
C++:プログラム言語の名前

本来は、同列にあつかうものではないと思う。

#C++/CLIはVC#みたいに別にしてほしいなぁと思う

いけやん 2008/08/06(水) 09:21:33
> VC++の本はC++言語の文法は理解していること前提
そうなんですか〜。だからC++の勉強必要なんですね。

> VC++6.0でないと駄目な理由が無い限りは最新の環境で勉強された方が
> よろしいかと思います。
> コンパイラもライブラリも最新の物の方が準拠率は高いです

なるほど、できるだけ最新の物でやった方がいいんですか〜。
今やっている内容が終わったら検討してみます。
ありがとうございます。


> 本来は、同列にあつかうものではないと思う。
別物なんですか〜。一緒みたいに扱った発言あったらすいませんでした。

たいちう 2008/08/06(水) 11:04:55
ところで、今、未解決の問題があるのですか?
ないなら解決してこのスレを閉じてはいかがでしょうか。

未解決の問題についても、当初の質問との直接的な関連が小さいようなら、
別スレで質問するのが良いと思います。

いけやん 2008/08/06(水) 11:24:17
[[解決]]
たいちうさん、ありがとうございます。

そうですねタイトルから、質問がずれてますね。
CStdioFileの使い方についての質問は解決したので、

今残っている、質問は新規スレで質問します。

後、解決したのはBlueさん、επιστημηさん
書いて頂いたサンプルで解決しました。

if ( !fin.Open("aaa.txt", CFile::modeRead, NULL ) )

CStdioFile fInStream(strFileName, CFile::modeRead | CFile::typeText);

みなさん、ありがとうございました。

IDO 2008/08/12(火) 16:53:34
移動
http://www.play21.jp/board/formz.cgi?
action=res&resno=20101&page=&id=dixq&rln=20112

古いスレッドには追加発言できません。