none
C#からsprintf RRS feed

  • 質問

  • いつもお世話になります。C#からsprintfのようにフォーマット付き文字列変換を行いたいのですが,String.Format等は使えないのです。フォーマットは客先から指定されており、C言語の%02d,%8.2f,%s...などといったフォーマット指定です。フォーマットの指定を.NETスタイルに自力で変換するのは避けたいです。C++等を経由すればできるかも知れませんが、unmanageの経験がほとんどなく、困っております。ご教授願えますでしょうか。

    環境WinXP,C#2.0, VS2005

    2006年9月4日 5:19

回答

  • おそらく、C#からそれらの関数は使えません。

    WindowsAPIのような関数であれば使えますが、C言語の関数は使うことはできないでしょう。
    (アンマネージ云々の話ではない。)

    どうしても使いたいのであれば、そのようなDLLやクラスライブラリをVC++で作ってください。

    2006年9月4日 6:39
  • 諸農です。

     Myon さんからの引用

    C++等を経由すればできるかも知れませんが、unmanageの経験がほとんどなく、困っております。

    セキュリティが強化されたsprintf関数がVC++2005で使えるようですので、C#から呼び出し可能なライブラリを作ってみるというのはいかがでしょうか?

    http://msdn2.microsoft.com/ja-jp/library/ce3zzk1k.aspx

     

    2006年9月4日 6:51
  • どうやってクラスライブラリを使うかはよくわかりませんけど、VC++で作るとしたら、
    C#側ではアンマネージドを意識しないように作られたほうがよいでしょう。

    つまり

    String^ Spritnf( String^ format, array< Object^ >^ obj )
    {
         wchar_t buff[ 256 ];
         va_list args;
         
         // objから args をつくる。作り方は忘れたw
    vswprintf( buff, PtrToStringChars( format ), args ); return gcnew String( buff ); }

    みたいなかんじに。

    2006年9月4日 7:12
  •  蒼の洞窟 さんからの引用

    // objから args をつくる。作り方は忘れたw

    ですが、できないっぽいですね。(知識不足なのか、わからん)

    ですので、面倒ですが、ループで回るか(swprintfもしくはswprintf_s)、最大個数分だけswitchさせるかですかね。
    (多分後者の方が簡単。)
    可変長リストというのがC#にない以上こうするしかないっぽ。

    2006年9月4日 7:25
  • やはり、C言語のsprintfのようにいくつもの要素を渡してというのは難しそうです。
    ですので、 %~ に対して,1つの要素を渡して変換という風にちょっとヘボいですが
    そんな感じにして、C#側で連結してあげるのはどうでしょうか?

    public ref class Class1
    {
    public:
        // int型用
        static String^ Sprintf( String^ format, int n )
        {
            wchar_t buff[ 256 ];
            swprintf_s( buff, 256, PtrToStringChars( format ), n );
    return gcnew String( buff ); } // short型用 // 略 // long型用 // 略 // float型用 // 略 // double型用 // 略 // int64型用 // 略 // String型用 static String^ Sprintf( String^ format, String^ str ) { wchar_t buff[ 256 ]; swprintf_s( buff, 256, PtrToStringChars( format ),
    PtrToStringChars( str ) ); return gcnew String( buff ); } }
    C#
    int n = 10;
    double d = 2.5;
    
    // 本当は "%2d%g" としたいが、できないので下のように1要素ずつ変換させる
    string s = Class1.Sprintf("%2d",n) + Class1.Sprintf("%g", d);
    
    2006年9月4日 7:58
  • うーむ。
    #include <vcclr.h>はしていますか?
    ただいまVS2005環境がないのでなんとも試しようがないのです。

    一度

    pin_ptr<const wchar_t> p = PtrToStringChars( format );
    swprintf_s( buff, 256, p, n );

    pin_ptrにかまさないとダメなのかも。

    2006年9月4日 8:17

すべての返信

  • おそらく、C#からそれらの関数は使えません。

    WindowsAPIのような関数であれば使えますが、C言語の関数は使うことはできないでしょう。
    (アンマネージ云々の話ではない。)

    どうしても使いたいのであれば、そのようなDLLやクラスライブラリをVC++で作ってください。

    2006年9月4日 6:39
  • ありがとうございます。やはり使えないのですね。そこでVS2005に入っているVC++を使ってDLLを作ってみたのですが、文字列の入出力の仕方がわからず困っております。C++側で以下のように組んだのですが、これをC#から使うことは可能でしょうか。宜しくお願い致します。


     public ref class Class1
     {
     public :

     void Sprintf( char* buf, const char* format, Object obj )
      {
       sprintf( buf, format, obj );
      }
     };

     

    2006年9月4日 6:48
  • 諸農です。

     Myon さんからの引用

    C++等を経由すればできるかも知れませんが、unmanageの経験がほとんどなく、困っております。

    セキュリティが強化されたsprintf関数がVC++2005で使えるようですので、C#から呼び出し可能なライブラリを作ってみるというのはいかがでしょうか?

    http://msdn2.microsoft.com/ja-jp/library/ce3zzk1k.aspx

     

    2006年9月4日 6:51
  • どうやってクラスライブラリを使うかはよくわかりませんけど、VC++で作るとしたら、
    C#側ではアンマネージドを意識しないように作られたほうがよいでしょう。

    つまり

    String^ Spritnf( String^ format, array< Object^ >^ obj )
    {
         wchar_t buff[ 256 ];
         va_list args;
         
         // objから args をつくる。作り方は忘れたw
    vswprintf( buff, PtrToStringChars( format ), args ); return gcnew String( buff ); }

    みたいなかんじに。

    2006年9月4日 7:12
  •  蒼の洞窟 さんからの引用

    // objから args をつくる。作り方は忘れたw

    ですが、できないっぽいですね。(知識不足なのか、わからん)

    ですので、面倒ですが、ループで回るか(swprintfもしくはswprintf_s)、最大個数分だけswitchさせるかですかね。
    (多分後者の方が簡単。)
    可変長リストというのがC#にない以上こうするしかないっぽ。

    2006年9月4日 7:25
  • >C#側ではアンマネージドを意識しないように作られたほうがよいでしょう。

    こんな風にできるんですね。ありがとうございます。理想的です。

    さて、va_listに行く前にコンパイルが通りません。

    vswprintf( buff, PtrToStringChars( format ), args );

    なのですが「2 オーバーロードのどれも、すべての引数の型を変換できませんでした」といわれてしまいます。初歩的な質問で申し訳ありませんが、VC++となると手が出ませんのでご教授願えますでしょうか。

    2006年9月4日 7:46
  • >C#から呼び出し可能なライブラリを作ってみるというのはいかがでしょうか?

    ありがとうございます。未熟のため引数の渡し方がわかりませんので、わかり次第挑戦したいと思います。

    2006年9月4日 7:52
  • やはり、C言語のsprintfのようにいくつもの要素を渡してというのは難しそうです。
    ですので、 %~ に対して,1つの要素を渡して変換という風にちょっとヘボいですが
    そんな感じにして、C#側で連結してあげるのはどうでしょうか?

    public ref class Class1
    {
    public:
        // int型用
        static String^ Sprintf( String^ format, int n )
        {
            wchar_t buff[ 256 ];
            swprintf_s( buff, 256, PtrToStringChars( format ), n );
    return gcnew String( buff ); } // short型用 // 略 // long型用 // 略 // float型用 // 略 // double型用 // 略 // int64型用 // 略 // String型用 static String^ Sprintf( String^ format, String^ str ) { wchar_t buff[ 256 ]; swprintf_s( buff, 256, PtrToStringChars( format ),
    PtrToStringChars( str ) ); return gcnew String( buff ); } }
    C#
    int n = 10;
    double d = 2.5;
    
    // 本当は "%2d%g" としたいが、できないので下のように1要素ずつ変換させる
    string s = Class1.Sprintf("%2d",n) + Class1.Sprintf("%g", d);
    
    2006年9月4日 7:58
  • 要素をひとつに限定するのは今のところ問題なさそうです。

    ただやはり同じところでコンパイルエラーとなってしまいます。

    swprintf_s( buff, 256, PtrToStringChars( format ), n );

    エラーメッセージは同じです。何か足りないのでしょうか。

     

    2006年9月4日 8:10
  • うーむ。
    #include <vcclr.h>はしていますか?
    ただいまVS2005環境がないのでなんとも試しようがないのです。

    一度

    pin_ptr<const wchar_t> p = PtrToStringChars( format );
    swprintf_s( buff, 256, p, n );

    pin_ptrにかまさないとダメなのかも。

    2006年9月4日 8:17
  • #include <vcclr.h>はやっていました。

    pin_ptr<const wchar_t> p = PtrToStringChars( format );でOKでした。

    あとはC#側で何とかなりそうです。

    ご親切に教えて頂きありがとうございました。

    本当に助かりました。感謝いたします。

    2006年9月4日 8:46
  • 申し訳ありませんが、関連でもう一問お願いしたいのです。(C++の話なので掲示板違いになりまして申し訳ありません)

    swprintf_sを使って上のようにやっておりましてうまく行ってるのですが、フォーマット文字列に変なものを入れるとswprintf_sで落ちてしまいます(%t等)。try-catchもやっていますがどうしても落ちるようです。エラーメッセージは「Debug Assertion Failed state != ST_INVALID」です。セキュリティが強化されたswprintf_sのヘルプを見るとフォーマットエラーは戻り値でエラーを返してくれると書いてあるのですが。宜しくお願い致します。

    2006年9月5日 8:38
  • swprintf_sは単にメモリ上のことだけなのかも。

    というか、swprintf_sを使うのはプログラマなので、そういう文字を使わなければいい話なのでは?
    もしかして、(プログラム上で)ユーザが入力した文字列(入力として何でもありの文字列)を渡しているんでしょうか?
    そうしたら、その文字列のチェックは必須ですよ。

    ちなみに、C言語の関数は例外を投げているわけではない(とおもう)のでtry~catchは無意味です。

    2006年9月5日 8:58
  • よく、MSDNをよんでみたところ、printf、_printf_l、wprintf、_wprintf_l  パラメータの検証より、

    無効なパラメータが見つかると、C ランタイムは、現在割り当てられている無効なパラメータ ハンドラを呼び出します。既定の無効なパラメータ ハンドラは、アクセス違反例外を発生させます。この場合、通常、実行が続行不能になります。デバッグ モードでは、アサーションも発生します。また、Watson クラッシュ レポートを既定の設定のまま有効にしている場合、アプリケーションがクラッシュし、Microsoft に対して分析用にクラッシュ ダンプを読み込むかどうかをたずねるメッセージが表示されます。

    この動作は、無効なパラメータ ハンドラを独自の関数に設定する _set_invalid_parameter_handler 関数を使用することにより、変更できます。指定した関数がアプリケーションを終了する場合、無効なパラメータを受け取った関数に制御が戻ります。これらの関数は通常、実行が停止し、エラー コードを返し、errno をエラー コードに設定します。多くの場合、errno 値と戻り値は両方とも EINVAL になり、無効なパラメータを示します。場合によっては、より具体的なエラー コードが返ります。たとえば、パラメータとして渡された無効なファイル ポインタを示す EBADF などです。errno の詳細については、「errno、_doserrno、_sys_errlist、および _sys_nerr」を参照してください。

    とありました。
    _set_invalid_parameter_handler のリンク先をよむと、ちゃんとerrnoで確認できるように記述でるようです。

    2006年9月5日 15:12
  • ありがとうございました。戻り値でエラーを取得することができました。結局以下のようなコードになりました(HTML上できれいに表示する方法がわからないので見にくくてすいません)。return "";なんてやってますがNULLを返せれば理想的なのですが・・・。

    また、フォーマット文字列はユーザーから渡されるものなのでチェックする必要がありました。



    #include <stdio.h>
    #include <vcclr.h>
    #include <stdlib.h>
    #include <crtdbg.h>  // For _CrtSetReportMode

     void myInvalidParameterHandler(const wchar_t* expression,const wchar_t* function, const wchar_t* file,unsigned int line, uintptr_t pReserved)
     {
      wprintf(L"Invalid parameter detected in function %s."
       L" File: %s Line: %d\n", function, file, line);
      wprintf(L"Expression: %s\n", expression);
     }

      // int型用
      static String^ Sprintf( String^ format, int value )
      {
       try
       {
        _invalid_parameter_handler oldHandler, newHandler;
        newHandler = myInvalidParameterHandler;
        oldHandler = _set_invalid_parameter_handler(newHandler);
        // Disable the message box for assertions.
        _CrtSetReportMode(_CRT_ASSERT, 0);
        int nRet;
        wchar_t buff[ 256 ];
        pin_ptr<const wchar_t> p = PtrToStringChars( format );
        nRet = swprintf_s( buff, 255, p, value );
        if( nRet < 0 )
        {
         return "";
        }
        return gcnew String( buff );
       }
       catch( Exception^ ex )
       {
        return "";
       }
      }

     

    2006年9月7日 1:07
  •  Myon さんからの引用
    return "";なんてやってますがNULLを返せれば理想的なのですが・・・。

    nullptrを返すことはできなかったかなぁ。
    また、クラスライブラリからまた例外を投げるというのもアリかなと。(出来るのかは確かではないが。)

     Myon さんからの引用
    HTML上できれいに表示する方法がわからないので見にくくてすいません

    BBコードと呼ばれるものをつかうと、きれいに(?)コードを表示できます。
    参考: http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=227443&SiteID=7

    2006年9月7日 1:22
  • nullptrを知りませんでした。bbcodeのリンクもありがとうございます。

     



    #include <stdio.h>
    #include <vcclr.h>
    #include <stdlib.h>
    #include <crtdbg.h>  // For _CrtSetReportMode

     void myInvalidParameterHandler(const wchar_t* expression,const wchar_t* function, const wchar_t* file,unsigned int line, uintptr_t pReserved)
     {
      wprintf(L"Invalid parameter detected in function %s."
       L" File: %s Line: %d\n", function, file, line);
      wprintf(L"Expression: %s\n", expression);
     }


      // int型用
      static String^ Sprintf( String^ format, int value )
      {
       try
       {
        _invalid_parameter_handler oldHandler, newHandler;
        newHandler = myInvalidParameterHandler;
        oldHandler = _set_invalid_parameter_handler(newHandler);
        // Disable the message box for assertions.
        _CrtSetReportMode(_CRT_ASSERT, 0);
        int nRet;
        wchar_t buff[ 256 ];
        pin_ptr<const wchar_t> p = PtrToStringChars( format );
        nRet = swprintf_s( buff, 255, p, value );
        if( nRet < 0 )
        {
         return nullptr;
        }
        return gcnew String( buff );
       }
       catch( Exception^ ex )
       {
        return nullptr;
       }
      }

     

    2006年9月7日 1:54
  • いまさらですけど、C#からでもCの可変個引数を可変個引数として渡せるようですよ。

    2007年8月5日 1:29