表や能等の下位コード5cに当たる文字と\との区別方法

[戻る]
みかん 2012/02/17(金) 16:17:46
いつもお世話になっております。初質問になりますが宜しくお願いします。

開発はVC6.0のMFCにて行っております。

CString strPath = _T("C:\DATA\表.txt") ;
CString strFileName ;
_splitpath( strPath, NULL, NULL, strFileName.GetBuffer(_MAX_FNAME), NULL)) ;
strFileName.ReleaseBuffer() ;

を実行した場合にstrFileNameには何も入っていない状態になります。
この現象はReleaseで実行した際にのみ発生し、debugでは正常に表が取得できます。

原因としては表の下位コードが\と同じの5cとなっているというところまでは分かってい
ます。

このように\と同じ文字コードの文字が入っているフルパスをフォルダ名やファイル名に
分割する場合、どの様な手法を取ると分割が行えるのでしょうか?

色々検索はしてみたのですが、自分の調べ方が悪いのか解決方法が見つからず困ってお
ります。

どうか宜しくお願い致します。

仲澤@失業者 2012/02/17(金) 16:58:51
setlocale( LC_ALL, "jpn");
を試してみましたか。

ITO 2012/02/17(金) 17:09:09
"C:\DATA\表.txt" は"C:\\DATA\\表.txt" ですね。

仲澤@失業者 2012/02/17(金) 17:12:37
ごめん、
> CString strPath = _T("C:\DATA\表.txt") ;
って、本来のコードでは
> CString strPath = _T("C:\\DATA\\表.txt") ;
になってますよね(確認)。

みかん 2012/02/19(日) 18:50:54
返信遅れてしまい申し訳ありません。

情報ありがとうございます。

setlocaleについてはどういうものか理解して使用を検討してみたいと思います。

ファイルパスについては"C:\\DATA\\表.txt"となっております。

AR 2012/02/20(月) 12:40:49
 パス文字の分解で、2バイト文字の処理は結構めんどくさいので私はUNICODEに変換し
て処理しています。(邪道かもしれませんし、速度的な優位性はありません)
 CW2A、CA2W(VC6だとW2A、A2Wだったかも)マクロを使うとかなり安易に使えますので、
参考までにどうぞ。

PATIO 2012/02/20(月) 13:17:18
ご質問の内容は、0x5cが出てきた時に'\'なのかトレイルバイトなのかを調べる方法で
しょうか?

上記の話なら文字列をバイト単位で先頭から順番に検査するしかないと思います。
トレイルバイトに0x5cが来ている場合、直前のバイトは必ず2バイトコードのリードバイトの
コードが存在していますから、1バイトずつ検査してリードバイトだった時は、次の1バイトを
トレイルバイトと認識して'\'ではないと判断すれば良いという話だと思います。
リードバイトかどうかを判定する関数はあったはずなので調べてみてください。

そういう話では無いという事でしたら、スルーしてください。

maru 2012/02/20(月) 13:38:45
ポイントは
> この現象はReleaseで実行した際にのみ発生し、debugでは正常に表が取得できます。

ですよね。

以下のコードでRelease/debugとも「表」が表示されました。
void func()
{
    CString strPath = _T("C:\\DATA\\表.txt");
    CString strFileName;
    _splitpath(strPath, NULL, NULL, strFileName.GetBuffer(_MAX_FNAME), 
NULL);
    strFileName.ReleaseBuffer();
    AfxMessageBox(strFileName);
}
つまりやり方は間違っていないはず。
みかんさんのコードで気になるところは_splitpath()の最後に余分な")"がついている
点。
実際には提示されたコードとは別のコードでやっているんですよね。
(エスケープ文字も抜けていたし。)
原因は提示されていない部分にあるのではないでしょうか?
現象が発生するコードをそのまま提示したら如何でしょう。

みかん 2012/02/20(月) 15:25:57
皆様、色々とありがとうございます。

setlocaleについてまだ把握し切れていませんが、軽くテストした結果では変化はありま
せんでした。

CW2A、CA2Wについてはまだ理解しきれていない為引き続き調査していこうと思います。

PATIO様の意見ですが、内容的にはそうなるわけですが、今回はファイルのフルパ
スからフォルダ名、ファイル名が分割するというのが目的になっています。
最終的には先頭から一つずつ調査していって分割する関数を作成するという事も視野に
いれて検討してみます。
リードバイトであった場合次のバイトを見てという方法は大変参考になったので是非使
用してみたいと思います。

maru様の意見通り、ReleaseとDebugでの違いもポイントになり、例文もかなり簡単なも
のに変換してあります。
以下実際のコードの流れが分かる様に一部抜き出したものになります。

/**************************************/
void CFileName::Main()
{
    CString strProg = _T("") ;
    // ここで取得したデータは".\\DATA\\WORK\\1表.TXT"となっています
    GetPrivateProfileString( セクション名, キー名, _T(""), strProg.GetBuffer
(MAX_PATH), MAX_PATH, パス ) ;

    // 関数へ飛ぶ
    GetData( strProg ) ;
}

BOOL CFileName::GetWorkSize( CString strProg )
{
    // dllにて拡張子を変更する
    CString    strCalcFile    = GetChangeExt( strProg, "INI" ) ;

    // 以下拡張子を変換したファイルを処理
    〜〜〜〜
    return( FALSE ) ;
}

// string = 元ファイル名 ext=変更拡張子
CString FAR PASCAL EXPORT GetChangeExt( CString *string, char *ext )
{
    // ファイル名を取り出す
    CString strDrive ;
    CString strDir ;
    CString strFName ;
    CString strExt ;
    _splitpath( *string,  strDrive.GetBuffer( _MAX_DRIVE ), 
strDir.GetBuffer( _MAX_DIR ), strFName.GetBuffer( _MAX_FNAME), strExt.GetBuffer
( _MAX_EXT ) ) ;
    strDrive.ReleaseBuffer() ;
    strDir.ReleaseBuffer() ;
    strFName.ReleaseBuffer() ;
    strExt.ReleaseBuffer() ;

    // 拡張子とファイル名をくっつける
    CString retSt = _T("") ;
    retSt.Format( _T("%s%s%s.%s"), strDrive, strDir, caFName, ext ) ;

    return( retSt ) ;
}

/**************************************/

みかん 2012/02/20(月) 15:34:49
申し訳ありません、コードを書き込む前に読み直したつもりでしたが再度読み直すと最
初の関数へ飛ぶ部分の関数名が間違っていました。

GetDataではなくGetWorkSizeになりますのでご了承ください。

tetrapod 2012/02/20(月) 16:41:44
なんだか判断に困るコードだ・・・ツッコミどころ多杉

MBCS なのか UNICODE なのか一度も発言中に明記されていないし
※GetChangeExt の引数は CString* なのか CString なのか判断に困るし
_MAX_PATH と MAX_PATH は意図して使い分けているのかどうか同上
TCHAR と char が混在しているし

警告レベルを上げて警告のある箇所を見直すだけで直る可能性が大とみた。
(発言内容から判断するに MBCS なのだろうから) UNICODE にしてみてエラー・警告の
出る部分を修正するともっとよいと思われる。

みかん 2012/02/20(月) 19:41:57
tetrapod様の発言通り間違いが多くて申し訳ありません。

文字コードはMBCSで、警告レベルは4になっており、最適化は無効(デバッグ時)にして
います。
ブラウザ情報やデバッグ情報は残さないように設定してあります。

問題の発生しているdllにてテストコードを書いても同様の結果になったのでそのコード
そのままコピーさせて頂きます。

/**************************************/
    CString strString = _T("C:\\DATA\\表.txt") ;
    // ファイル名を取り出す
    CString strDrive ;
    CString strDir ;
    CString strFName ;
    CString strExt ;
    CString strData = *string ;

    USES_CONVERSION ;
    CString strUString = A2W( strString ) ;

    _splitpath( strUString,  strDrive.GetBuffer( _MAX_DRIVE ), 
strDir.GetBuffer( _MAX_DIR ), strFName.GetBuffer( _MAX_FNAME ), 
strExt.GetBuffer( _MAX_EXT ) ) ;
    strDrive.ReleaseBuffer() ;
    strDir.ReleaseBuffer() ;
    strFName.ReleaseBuffer() ;
    strExt.ReleaseBuffer() ;
/**************************************/

Releaseモードでの取得データ
strDrive=C:
strDir=\DATA\表
strFName=
strExt=.txt

Debugモードでの取得データ
strDrive=C:
strDir=\DATA\
strFName=表
strExt=.txt

仲澤@失業者 2012/02/21(火) 10:07:04
UNICODEとMBCSが混ざっちゃってますね。
元のstrStringを、そのまんま_splitpath()に渡せば、
原理的には正しく動くはずです。

//  USES_CONVERSION ; いらない
//  CString strUString = A2W( strString ) ;いらない
//元の文字列を渡す↓
    _splitpath( strString,  strDrive.GetBuffer( _MAX_DRIVE ), ・・・

仲澤@失業者 2012/02/21(火) 10:22:55
っつうか、CStringの使い方がアウトなので、
リリースの場合ソースのまんまじゃ確かめようがないですね(笑)。
素直に、char配列にしてから試してくださいね。

ITO 2012/02/21(火) 13:15:25
> 素直に、char配列にしてから試してくださいね。
これに賛成ですね。
MBCSでいいんですよね。
だと、最後にCStringに転送すればいいのですよね。
MBCSでいいのなら文字操作はchar配列のほうがいいように思います。
ただ、char配列は文字数を固定して扱ったほうがいいです。
(余った部分は'\0'で)

ITO 2012/02/21(火) 13:21:30
追記、
 VC6.0の"_splitpath"ってUNICODE大丈夫ですか?
 存知の方お願いします。
 VC2005は大丈夫ですね。

maru 2012/02/21(火) 14:44:49
ご報告。

Dll側
extern "C" BOOL PASCAL EXPORT ExportedFunction(char *name)
{
    CString strString = _T("C:\\DATA\\表.txt") ;
    // ファイル名を取り出す
    CString strDrive ;
    CString strDir ;
    CString strFName ;
    CString strExt ;
    CString strData = strString ;

    _splitpath( strString,  strDrive.GetBuffer( _MAX_DRIVE ), 
    strDir.GetBuffer( _MAX_DIR ), strFName.GetBuffer( _MAX_FNAME ), 
    strExt.GetBuffer( _MAX_EXT ) ) ;
    strDrive.ReleaseBuffer() ;
    strDir.ReleaseBuffer() ;
    strFName.ReleaseBuffer() ;
    strExt.ReleaseBuffer() ;
    strcpy(name, strFName);
    return TRUE;
}

呼び出し元(exe)
    char    name[256];
    ExportedFunction(name);
    AfxMessageBox(name);

結果:Release/Debugどちらも問題なくメッセージボックスに「表」が表示される。
(文字コードはVC6のデフォルトのまま=MBCS)

ということで、提示されている以外のコードに問題がある、に一票。

tetrapod 2012/02/21(火) 15:01:11
っていうか TCHAR(=CString) を使うのなら _splitpath など使っちゃダメダメ
_tsplitpath でなきゃ。

_splitpath に char を渡すのであれば問題ない
_wsplitpath に wchar_t を渡すのであれば問題ない
char と wchar_t が混在するのは論外 (今回の提示例のように)

maru 2012/02/21(火) 15:02:46
ついでに みかんさん の関数 GetChangeExt で実験

DLL側
CString FAR PASCAL EXPORT GetChangeExt( CString *string, char *ext )
{
    // ファイル名を取り出す
    CString strDrive ;
    CString strDir ;
    CString strFName ;
    CString strExt ;
    _splitpath( *string,  strDrive.GetBuffer( _MAX_DRIVE ), 
strDir.GetBuffer( _MAX_DIR ), strFName.GetBuffer( _MAX_FNAME), strExt.GetBuffer
( _MAX_EXT ) ) ;
    strDrive.ReleaseBuffer() ;
    strDir.ReleaseBuffer() ;
    strFName.ReleaseBuffer() ;
    strExt.ReleaseBuffer() ;

    // 拡張子とファイル名をくっつける
    CString retSt = _T("") ;
    retSt.Format( _T("%s%s%s.%s"), strDrive, strDir, strFName/*caFName*/, 
ext ) ;/*提示されたコードではコンパイルエラーとなるので変数を変更*/

    return( retSt ) ;
}

呼び出し元:
    CString strPath = _T("C:\\DATA\\表.txt");
    CString changedPath = GetChangeExt(&strPath, "INI");
    AfxMessageBox(changedPath);

結果:問題なし

maru 2012/02/21(火) 15:39:09
補足:
> 結果:問題なし
結果として必要な文字列が得られたということで、プログラムが正しいと言っているわ
けではありません。
正しくは tetrapod さんがおっしゃっているように、char/wchar_t/TCHARに合わせて適
切に関数を使い分ける必要があります。
# いうまでもないことですが。

PATIO 2012/02/21(火) 16:22:55
VC6を使っているという時点で色々突っ込み所はあると思います。
今までのソフトのメンテナンス用と言う話ならわかるんですが、
もし改めて作成しようとしているのであれば、VC6を使うのはどうかなと。

VC6は現状のC++の規格からすると相当古い実装ですから、
今時のC++のサンプルなんかもコンパイル通らなかったりしそうですし。

MFCを使える環境を用意するとなるとお金が掛かるからと言う話で
なおかつ個人でやっていると言うなら仕方ないかなと思いますけれど、
もし仕事でやっているならバージョンアップをしないと結構つらいと思います。

さて、VC6を使っていたとしてもTCHAR、char、WCHAR等の使い分けに関しては
何も変わっていないと思いますから、もしその辺の知識に自身が無いのであれば、
きちんと勉強して確認された方が良いと思います。

ReleaseとDebugで挙動が違うと言うのは基本的に自分のコーディングに
問題がある事が殆どだと思います。
場合によっては別な所でメモリを壊している事が原因だったりする事も
あると思いますから、コードの見直しを考えた方が良いかもしれません。

仲澤@失業者 2012/02/21(火) 16:30:20
maruさん、おつかれ様です。
自分も実験してみました(たまたまVC6のPCが空いてた)。
char配列を使いましたが、Releaseモードで正常に「表」が
取れますね。

(余禄)久しぶりに使ってみるとVC6のソースファイルの選択(MDI)が
   結構使いづらいし、リリースってどうやって設定するんだったっけ
   とかが・・・(vv;)。

gak 2012/02/21(火) 18:15:18
> 原因としては表の下位コードが\と同じの5cとなっているというところまでは
_splitpath() 呼ぶ前に

    _setmbcp(_MB_CP_OEM);

を入れてみたら? 多分マルチバイト文字関連のコードページがおかしな指定になってん
じゃない?

ただ、これで正常に動いたとしても「何故 Release でのみ症状が起きてるのか」の解説
は俺にはできんが
# プロジェクト設定等も含めて、何処かに”何か”があり其れに起因してるんだろうけど

> CString FAR PASCAL EXPORT GetChangeExt( CString *string, char *ext )
参考: PathRenameExtension() というファイルパスの拡張子を変更するための API が既
存したりする。

bun 2012/02/22(水) 08:54:53
今さらですが、VC6の頃、MFCのこんなバグに悩まされたことがあります。
  http://win32lab.com/memo/index.cgi?no=12&reno=no&oya=12&mode=msgview&page=0

今回の件と直接は関係がないのですが、参考までに。

みかん 2012/02/23(木) 15:45:32
様々なコメントありがとうございます。

指摘されているcharやwchar_tの違いについて正確に把握できていなく、勉強不足という
事を痛感いたしました。
ここは勉強し直したいと思います。

問題の発生している部分でCstringをcharしたり色々こちらで試しましたが変化がありま
せんでした。

ただ、_setmbcp(_MB_CP_OEM);
を宣言した所ファイル名を正確に取得する事に成功致しました。

これを元に何故Releaseモードでのみ問題が発生するか引き続き調査して行きたいと思い
ます。

皆様、ご協力ありがとうございました。

私だ [HomePage] 2012/03/22(木) 23:36:57
ぐっじょぶ(Д゜)ノシ☆ http://nn7.biz

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