4

sprintf と cstrings を使用してファイル名を作成する「デモ」コードが大量にあります。非常に長いファイル名を許可し、よりクリーンな構文を提供するために、これを c++ 文字列に置き換えたいと思います。

したがって、基本的に次のようなブロックを変換する必要があります

ofstream some_file;
char filename[100];
sprintf(filename,"%s/soln%i.dat",a.c_str(), b);
some_file.open(filename);

のようなものに

ofstream some_file((a + "soln" + to_string(b) + ".dat").c_str());

ただし、これは多くの場所で使用されているため、ある種の自動変換を使用したいと考えています (つまり、sed 式、emacs 関数/マクロ、ビジュアル スタジオなど)。(私が見ることができる)困難は次のとおりです。

  1. sprintf ステートメントの内容は何でもかまいません。(つまり、sprintf ステートメントを解析する必要がありますか?)

  2. some_file と filename はどこでも宣言でき、任意の名前を付けることができ、複数のファイルへの出力に再利用されることがよくあります。(つまり、C++ をパースする必要があるのでしょうか?)

これは可能/実現可能ですか?

おまけとして、(ofstream コンストラクターを変更せずに) 文字列に対して .c_str() を呼び出さなくて済むようにできますか?

4

2 に答える 2

2

Yassnippet を使用しない、より単純なものを次に示します。

(defun sprintf-to-ofstream (file)
  (interactive "sFile to print to: ")
  (back-to-indentation)
  (let* ((start (point))
         arglist
         sprintf-args
         ofstream-args
         (end 
          (save-excursion
            (move-end-of-line 1)
            (point)))
         (line (buffer-substring-no-properties start end)))
    (unless (string= (substring line 0 7) "sprintf")
      (error "No `sprintf' at this line"))
    (setq arglist
          (split-string
           (substring line (1+ (position ?\( line))
                      (position ?\) line :from-end t))
           "\\s-*,\\s-*")
          sprintf-args
          (split-string (substring (cadr arglist) 1 -1)
                        "%[^[:alpha:]%#]*[[:alpha:]%#]")
          ofstream-args
          (with-output-to-string
            (princ (concat "\"" (car sprintf-args) "\""))
            (setq sprintf-args (cdr sprintf-args))
            (dotimes (i (length sprintf-args))
              (princ " + ")
              (when (< (+ i 2) (length arglist))
                (princ (nth (+ i 2) arglist))
                (princ " + "))
              (princ (concat "\"" (nth i sprintf-args) "\"")))))
    (kill-region start end)
    (insert (concat "ofstream " file "((" ofstream-args ").c_str());" ))))

好きなキーにバインドして、次のように使用できます。

  • sprintfたとえば を実行して、置換したい行にポイントを移動してから、M-%sprintfこの関数を呼び出します。メッセージを出力するファイルの名前を指定するように求められます (おそらく、もっと複雑にして、ofstream が宣言された場所を上方向に検索することもできますが、任意の場所、それは無駄な努力のように聞こえます)。
  • を押すRETと、関数は行を次のように変更します。
ofstream foo(("" + a.c_str() + "/soln" + b + ".dat").c_str());

あなたの元の例を考えてください。明らかに、to_string()引数の周りに追加する可能性が高いと思われる場合は、それを行うのは簡単です。もう少し努力すれば、空の文字列の連結も簡単に回避できますが、場合によっては効果が変わる可能性があります /演算子の解釈+なので、このままにしておくことにしました。置換された文字列の不確かな場所にインタラクティブにジャンプし、事前に定義された置換もできるように、今日の少し後にそれを yassnippet スクリプトにしようとします。

于 2012-10-30T09:37:23.817 に答える
2

Boost Formatライブラリを使用します。C++ で printf 型のフォーマットを実装し、C++ 文字列を出力として生成します。呼び出しは似ていますが、少し異なります。あなたの例は次のようになります。

string fname = boost::format("%s/soln%i.dat") % a % b;
ofstream some_file(fname.c_str());

したがって、基本的には、char バッファーを識別し、それを文字列に置き換える必要があります。sprintf ステートメントを特定し、それを引数にトークン化し (引用符などに注意しながら)、最初の引数を boost::format でラップして、 の代わりに % を使用して書き直します。少し気が遠くなるように聞こえますが、不可能ではありません。より大きな問題は、すべてのブロックがそのように見えるとは限らず、一般的にこれらの状況を特定するのは非常に難しいことです.

于 2012-10-29T15:43:41.980 に答える