none
フォント名一覧を取得する時、文字の比較をしたい RRS feed

  • 質問

  • 開発環境 VisualStudio2008 、VisualC++、WindowsSDK開発 OS:Windows7

    フォント名一覧を取得したい為、EnumFontFamiliesEx関数のためのコールバック関数を作っています。
    文字列はマルチバイト対応で開発しています。
    マルチバイトの文字処理がよく分からないので質問させて頂きます。
    具体的なコードは以下の物です。

    //フォントの一覧を取得する
    int CALLBACK EnumFontFamilisExProc(ENUMLOGFONTEX *lpElfe,NEWTEXTMETRICEX *lpNtme,int fontType,LPARAM lParam)
    {
     std::wstring font_name= lpElfe->elfLogFont.lfFaceName;
     
     if(font_name.compare(TEXT("@"))) ←【先頭に@の文字がある場合はリストに加えないとしたい】
     {
      SendDlgItemMessage(hConfigDiialog,IDC_LIST_FONT,LB_ADDSTRING,0,(LPARAM)lpElfe->elfLogFont.lfFaceName);
     }
     return TRUE;
    }
    //---ここまで


    上記の if(font_name.compare(TEXT("@"))) ←【先頭に@の文字がある場合はリストに加えないとしたい】
    という処理ですがこの場合分けする場合の処理方法を教えてくださいませんでしょうか。
    2009年11月9日 10:45

回答

  • みんななんでそうするんだろう。

    wstring font_name = _T("@MS UI Gothic");
      ↓
    wstring font_name = L"@MS UI Gothic";
    wstringはwchar_tと決まっているので、TEXT()や_T()で切り替えるべきではありません。

    if ( font_name.substr(0, 1).compare(L"@") == 0 )
      ↓
    if( font_name[0] == L'@' )
    今回文字列比較したいわけではなく、先頭の1文字を見たいだけなので、直接wchar_tを取り出せばいいです。


    STLにはWin32 APIのようなTCHARによるUNICODE / ANSI切り替えがありません。
    wstringを使うのでしたら、EnumFontFamiliesEx / EnumFontFamExProcではなく、EnumFontFamiliesExW / EnumFontFamExProcWを使った方がいいでしょう。
    • 回答としてマーク 田中さん 2009年11月11日 1:03
    2009年11月10日 12:20
  • STL なら以下のようになりますね。

    #include "stdafx.h"
    #include <string>
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        wstring font_name = _T("@MS UI Gothic");
        if ( font_name.substr(0, 1).compare(L"@") == 0 ) 
        {
            printf( "このフォントは先頭に @ を含んでいます。\n" );
        }
        return 0;
    }
    string.substr() を使って先頭一文字を切り出した後、string.compare() で比較すればいいです。

    #超久々に STL を使ってみた。(^^;
    • 回答としてマーク 田中さん 2009年11月9日 17:20
    2009年11月9日 13:55
  • どこかのサイトで、文字の処理はCStringの方が使いやすいとのコメントを見たのですが
    #include <atlstr.h>  (非MFC)
    をインクルードしてCStringで処理するのと、どちらがベストなんでしょうか?
    どの文字列クラスを使うかというのはいろんな考え方がありますので、一概にどれがベストと言えないと思いますが、
    basic_string<>を前提としたライブラリなどを使うつもりがなくて、
    atlstr.hを使えない環境にコードを移植する可能性がないのであれば、
    CStringを使うのがいいのではないでしょうか。
    • 回答としてマーク 田中さん 2009年11月11日 8:29
    2009年11月11日 6:27

すべての返信

  • Standard C++ Library は無知ですが、std::wstring.compare 関数は文字列の比較ではないかと。

    font_name の文字列 と文字 @ の比較になっている気がします ( いや、TEXT("@") ですから、TEXT('@') と TEXT('\0') の文字列ですが ) 。この場合、わざわざ std::wstring を使わずに

    _mbsnicmp(lpElfe->elfLogFont.lfFaceName, TEXT("@"), 1) ではどうでしょうか?

    # 後、MBCS 文字列なのですから、std を使うのなら string ではないのかと思います・・・。


    追記

    半角の @ と全角の @ の違いもあるかもしれません。

    if (0 == _mbsnicmp(lpElfe->elfLogFont.lfFaceName, TEXT("@"), 1) || 0 == _mbsnicmp(lpElfe->elfLogFont.lfFaceName, TEXT("@"), 1)) が正解なのかな・・・。
    2009年11月9日 12:17
  • STL なら以下のようになりますね。

    #include "stdafx.h"
    #include <string>
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        wstring font_name = _T("@MS UI Gothic");
        if ( font_name.substr(0, 1).compare(L"@") == 0 ) 
        {
            printf( "このフォントは先頭に @ を含んでいます。\n" );
        }
        return 0;
    }
    string.substr() を使って先頭一文字を切り出した後、string.compare() で比較すればいいです。

    #超久々に STL を使ってみた。(^^;
    • 回答としてマーク 田中さん 2009年11月9日 17:20
    2009年11月9日 13:55

  • 確かに、このコードで動作しました。
    回答を頂いた皆様、ありがとうございました。

    2009年11月9日 17:24
  • みんななんでそうするんだろう。

    wstring font_name = _T("@MS UI Gothic");
      ↓
    wstring font_name = L"@MS UI Gothic";
    wstringはwchar_tと決まっているので、TEXT()や_T()で切り替えるべきではありません。

    if ( font_name.substr(0, 1).compare(L"@") == 0 )
      ↓
    if( font_name[0] == L'@' )
    今回文字列比較したいわけではなく、先頭の1文字を見たいだけなので、直接wchar_tを取り出せばいいです。


    STLにはWin32 APIのようなTCHARによるUNICODE / ANSI切り替えがありません。
    wstringを使うのでしたら、EnumFontFamiliesEx / EnumFontFamExProcではなく、EnumFontFamiliesExW / EnumFontFamExProcWを使った方がいいでしょう。
    • 回答としてマーク 田中さん 2009年11月11日 1:03
    2009年11月10日 12:20
  • みんななんでそうするんだろう。

    wstring font_name = _T("@MS UI Gothic");
      ↓
    wstring font_name = L"@MS UI Gothic";
    wstringはwchar_tと決まっているので、TEXT()や_T()で切り替えるべきではありません。

    if ( font_name.substr(0, 1).compare(L"@") == 0 )
      ↓
    if( font_name[0] == L'@' )
    今回文字列比較したいわけではなく、先頭の1文字を見たいだけなので、直接wchar_tを取り出せばいいです。


    STLにはWin32 APIのようなTCHARによるUNICODE / ANSI切り替えがありません。
    wstringを使うのでしたら、EnumFontFamiliesEx / EnumFontFamExProcではなく、EnumFontFamiliesExW / EnumFontFamExProcWを使った方がいいでしょう。

    うを。鋭い御指摘有難うございます。
    まだまだ修行が足りんですね。。。(--;
    2009年11月10日 13:36
  • TCHAR, _T 等や、Windows API の A/W の分岐があるものは、プロジェクトのプロパティに設定されている文字セットによって変動します。
    http://msdn.microsoft.com/ja-jp/library/c426s321.aspx

    佐祐理さんが指摘されているように、std::string, std::wstring を利用するのであれば、明示的に A または W バージョンを呼ぶ等、きちんと意識してコードを書きましょう。
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年11月10日 14:04
    モデレータ
  • 私も ひらぽん さんと同じ間違いをしていますね。というより、よりタチの悪いバグが発生する可能性をはらんだコードですね・・・。

    LOGFONT のドキュメントによると
    http://msdn.microsoft.com/en-us/library/ms901140.aspx

    lfFaceName メンバは TCHAR で宣言されていますから、

    _mbsnicmp(lpElfe->elfLogFont.lfFaceName, TEXT("@"), 1)

    では文字セットが変えられた場合におかしなことになりますよね・・・。

    _tcsnicmp(lpElfe->elfLogFont.lfFaceName, TEXT("@"), 1)


    こうするべきでした。佐祐理 さんのアドバイス、非常に有益に感じます。ありがとうございました。
    2009年11月10日 16:13
  • 補足ありがとうございます。

    普通のプロジェクトですと debug / release 2つの構成があります。
    私の場合、それに加えて UNICODE ⇔ ANSIの反対側の構成も用意して、反対側でもビルドできるか確認しています。コーディングは面倒になりますが、たまに変なバグを見つけて修正することもあります。

    STLは悪くはありませんが、TCHARに対応していないので、個人的にはWin32 APIと組み合わせるときはATL/MFCのCStringを使っています。


    ついでに
    wstring font_name = _T("@MS UI Gothic");
      ↓
    wstring font_name = L"@MS UI Gothic";
      ↓
    wstring font_name( L"@MS UI Gothic" );
    代入より、パラメータ付きのコンストラクタを呼んだ方がいいですね。
    2009年11月10日 22:45
  • 文字列はマルチバイト対応で開発しています。
    マルチバイト対応としていますが、MBCSのことではなくて、UNICODEということでしょうか。
    マルチバイト文字(unsigned char)とワイド文字(wchar_t)は用語としてちゃんと正しく使った方がいいと思いますよ。
    2009年11月11日 0:52
  • すみません。
    この場合、UNICODEの対応の事だったと思います。
    となると、上記のコードは、どれを使えばよいのでしょうか?
    2009年11月11日 1:05
  • UNICODEなら、STLの文字列クラスはstd::wstring、文字はワイド文字で型はwchar_t で、
    EnumFontFamiliesExW() / EnumFontFamExProcW()を使うということになります。

    ただ、UNICODE/MBCS両方に対応することを意識して書くのが普通ですので、

    STLの文字列クラスなら、std::basic_string<TCHAR>、文字の型はTCHARで、
    EnumFontFamiliesEx() / EnumFontFamExProc() と書いておくのがいいと思います。
    2009年11月11日 2:35
  • すみません。いまひとつだけ、お尋ねさせてください。

    私の参考にしている書籍ではstd::string に文字を入力していました。
    VisualStudio2008ではUNICODEで文字を処理しなくてはならないので
    std::wstrinfに書き換えて上記のコードにしました。

    どこかのサイトで、文字の処理はCStringの方が使いやすいとのコメントを見たのですが
    #include <atlstr.h>  (非MFC)
    をインクルードしてCStringで処理するのと、どちらがベストなんでしょうか?
    2009年11月11日 4:26
  • どこかのサイトで、文字の処理はCStringの方が使いやすいとのコメントを見たのですが
    #include <atlstr.h>  (非MFC)
    をインクルードしてCStringで処理するのと、どちらがベストなんでしょうか?
    どの文字列クラスを使うかというのはいろんな考え方がありますので、一概にどれがベストと言えないと思いますが、
    basic_string<>を前提としたライブラリなどを使うつもりがなくて、
    atlstr.hを使えない環境にコードを移植する可能性がないのであれば、
    CStringを使うのがいいのではないでしょうか。
    • 回答としてマーク 田中さん 2009年11月11日 8:29
    2009年11月11日 6:27
  • 加えるなら

    C++の cin / cout 的なstreamを使うならstring / wstringですが、C的にprintf系を使うならCString…もしくはクラスを使わない、でしょうか。
    2009年11月11日 9:38