通常、cout
とcerr
を使用して、コンソールにテキストを書き込みます。ただし、古くなったprintf
ステートメントを使用する方が簡単な場合があります。出力をフォーマットする必要があるときに使用します。
これを使用する場所の1つの例は次のとおりです。
// Lets assume that I'm printing coordinates...
printf("(%d,%d)\n", x, y);
// To do the same thing as above using cout....
cout << "(" << x << "," << y << ")" << endl;
cout
を使用して出力をフォーマットできることは知っていますが、printf
の使用方法は既に知っています。 printf
ステートメントを使用しない理由はありますか?
cin
とcout
を最初に学習し、その後printf
を学習する生徒は、printf
(または通常はfprintf
)を圧倒的に好む。私自身、printf
モデルが十分に読みやすいため、他のプログラミング言語に移植できました。 Olivier Danvy もあり、だれがそれをタイプセーフにしました。
printf
の呼び出しを型チェックできるコンパイラがある場合、C++でfprintf
とその友人を使用しない理由はありません。
免責事項:私はひどいC++プログラマです。
プログラムの国際化を希望する場合は、iostreamに近づかないでください。問題は、iostreamで行われているように、文が複数のフラグメントで構成されている場合、文字列を適切にローカライズできないことです。
メッセージフラグメントの問題に加えて、順序付けの問題もあります。生徒の名前と成績の平均点を印刷するレポートを考えてみましょう。
std::cout << name << " has a GPA of " << gpa << std::endl;
これを別の言語に翻訳する場合、他の言語の文法では、名前の前にGPAを表示する必要がある場合があります。私の知る限り、iostreamsは補間された値を並べ替える方法がありません。
両方の長所(タイプセーフティとi18n対応)が必要な場合は、 Boost.Format を使用します。
POD以外のprintf
を試行すると、未定義の動作が発生します。
struct Foo {
virtual ~Foo() {}
operator float() const { return 0.f; }
};
printf ("%f", Foo());
std::string foo;
printf ("%s", foo);
上記のprintf呼び出しは、未定義の動作をもたらします。コンパイラーは実際に警告する場合がありますが、これらの警告は標準では必要ではなく、実行時にのみ認識されるフォーマット文字列では不可能です。
IOストリーム:
std::cout << Foo();
std::string foo;
std::cout << foo;
自分で判断してください。
struct Person {
string first_name;
string second_name;
};
std::ostream& operator<< (std::ostream &os, Person const& p) {
return os << p.first_name << ", " << p.second_name;
}
cout << p;
cout << p;
some_file << p;
C:
// inline everywhere
printf ("%s, %s", p.first_name, p.second_name);
printf ("%s, %s", p.first_name, p.second_name);
fprintf (some_file, "%s, %s", p.first_name, p.second_name);
または:
// re-usable (not common in my experience)
int person_fprint(FILE *f, const Person *p) {
return fprintf(f, "%s, %s", p->first_name, p->second_name);
}
int person_print(const Person *p) {
return person_fprint(stdout, p);
}
Person p;
....
person_print(&p);
Cで適切な呼び出し引数/署名を使用する際の注意点(例:person_fprint(stderr, ...
、person_fprint(myfile, ...
)、C++では、「FILE
- argument」は式から自動的に「派生」します。この派生のより正確な同等物は、実際には次のようになります。
FILE *fout = stdout;
...
fprintf(fout, "Hello World!\n");
person_fprint(fout, ...);
fprintf(fout, "\n");
Person定義を再利用します。
cout << boost::format("Hello %1%") % p; cout << boost::format("Na %1%, sei gegrüßt!") % p; printf ("Hello %1$s, %2$s", p.first_name.c_str(), p.second_name.c_str()); printf ("Na %1$s, %2$s, sei gegrüßt!", p.first_name.c_str(), p.second_name.c_str());
自分で判断してください。
これは、今日(2017年)にはあまり意味がありません。たぶん直感的な感覚かもしれませんが、I18Nは平均的なCまたはC++プログラマーによって日常的に行われるものではありません。さらに、それはとにかく解剖学の痛みです。
一貫してiostreamを使用する場合、次のことができます。
std::ios::sync_with_stdio(false);
そして、優れたコンパイラーで同等のランタイムを獲得します。
#include <cstdio>
#include <iostream>
#include <ctime>
#include <fstream>
void ios_test (int n) {
for (int i=0; i<n; ++i) {
std::cout << "foobarfrob" << i;
}
}
void c_test (int n) {
for (int i=0; i<n; ++i) {
printf ("foobarfrob%d", i);
}
}
int main () {
const clock_t a_start = clock();
ios_test (10024*1024);
const double a = (clock() - a_start) / double(CLOCKS_PER_SEC);
const clock_t p_start = clock();
c_test (10024*1024);
const double p = (clock() - p_start) / double(CLOCKS_PER_SEC);
std::ios::sync_with_stdio(false);
const clock_t b_start = clock();
ios_test (10024*1024);
const double b = (clock() - b_start) / double(CLOCKS_PER_SEC);
std::ofstream res ("RESULTS");
res << "C ..............: " << p << " sec\n"
<< "C++, sync with C: " << a << " sec\n"
<< "C++, non-sync ..: " << b << " sec\n";
}
結果 (g++ -O3 synced-unsynced-printf.cc
、./a.out > /dev/null
、cat RESULTS
):
C ..............: 1.1 sec
C++, sync with C: 1.76 sec
C++, non-sync ..: 1.01 sec
裁判官...自分。
Variadicテンプレートのおかげで、C++ 11でタイプセーフでI18Nに優しいprintfを使用できます。そして、ユーザー定義のリテラルを使用して、それらを非常に、非常に高性能にすることができます。つまり、完全に静的なインカネーションを書くことが可能になります。
概念実証があります 。当時、C++ 11のサポートは今ほど成熟していませんでしたが、アイデアは得られました。
// foo.h
...
struct Frob {
unsigned int x;
};
...
// alpha.cpp
... printf ("%u", frob.x); ...
// bravo.cpp
... printf ("%u", frob.x); ...
// charlie.cpp
... printf ("%u", frob.x); ...
// delta.cpp
... printf ("%u", frob.x); ...
後で、あなたがしなければならないデータが非常に大きくなります
// foo.h
...
unsigned long long x;
...
これを維持し、バグをなくすことは興味深い演習です。特に、他の非結合プロジェクトがfoo.hを使用する場合。
バグの可能性:特にミックスでユーザー入力ベース文字列を投げる場合、printfでミスを犯すスペースがたくさんあります(I18Nチームのことを考えてください) 。このようなすべてのフォーマット文字列を適切にエスケープするように注意する必要があります。正しい引数などを必ず渡す必要があります。
IO-Streamsは私のバイナリを大きくします:これが保守性、コード品質、再利用性よりも重要な問題である場合、(問題を検証した後!) printfを使用します。
Boost :: formatを使用します。型の安全性、std :: stringのサポート、printfのようなインターフェイス、coutを使用する機能、その他多くの優れた機能があります。あなたは戻りません。
Fい<<cout<<
構文。
まったく理由はありません。古き良きCライブラリはまだ有効ですが、C++ライブラリのみを使用するように人々を駆り立てるのは、単なる奇妙なイデオロギーだと思います。私はC++の男で、C関数もよく使います。それらに問題はなかった。
ストリームは標準的な方法です。このコードをprintf
で動作させてみてください:
template <typename T>
void output(const T& pX)
{
std::cout << pX << std::endl;
}
幸運を。
つまり、型をostream
'sに出力できるようにする演算子を作成することができ、面倒なく他の型と同じように使用できます。 printf
は、C++、またはより具体的にはテンプレートの一般性に適合しません。
使いやすさ以上のものがあります。一貫性もあります。私のすべてのプロジェクトで、cout(およびcerr
とclog
)を使用してファイルに出力することもできます。 printf
を使用する場合、すべてをスキップします。さらに、一貫性自体が良いことです。 cout
とprintf
の混合は、完全に有効ですが、見苦しいです。
オブジェクトがあり、それを出力可能にしたい場合、これを行う最もクリーンな方法はオーバーロードoperator<<
そのクラス。では、printf
をどのように使用しますか? cout
とprintf
が混在したコードになってしまいます。
本当にフォーマットが必要な場合は、ストリームインターフェイスを維持しながらBoost.Formatを使用します。一貫性およびフォーマット。
Printfを使用します。 C++ストリームを使用しないでください。 printfは、はるかに優れた制御(浮動小数点精度など)を提供します。通常、コードは短く、読みやすくなります。
Google C++スタイルガイド 同意します。
ロギングインターフェイスで必要な場合を除き、ストリームを使用しないでください。代わりにprintfのようなルーチンを使用してください。
ストリームの使用にはさまざまな長所と短所がありますが、この場合は、他の多くの場合と同様に、一貫性が議論に勝ります。コードでストリームを使用しないでください。
全体的に私は同意します(特に複雑な書式設定が必要な場合は<<構文が嫌いです)
しかし、安全面を指摘する必要があります。
printf("%x",2.0f)
printf("%x %x",2)
printf("%x",2,2)
おそらくコンパイラーには気付かれませんが、アプリがクラッシュする可能性があります。
ニーズや好みに合ったものを使用してください。 printfに慣れている場合は、ぜひ使用してください。あなたがiostreamに満足しているなら、それらに固執してください。要件に最適な組み合わせと組み合わせてください。結局のところ、これはソフトウェアです。良い方法と悪い方法がありますが、たった1つの方法しかありません。
共有してお楽しみください。
私はprintfが好きではありません。タイプセーフがないため、使用するのが危険になります。さらに、書式指定子を覚えておく必要があります。賢明に正しいことを行うテンプレート化された演算子は、はるかに優れています。したがって、私は常にC++でC++ストリームを使用します。
確かに、多くの人がotherの理由でprintfを好んでいます。
私はしばしばprintf()
を使用することを「後戻り」しますが、より頻繁にsnprintf()
を使用してフォーマットされた出力を容易にします。 C++でプログラミングするとき、このラッパーを使用します(上記の例を使用するため)cout << format("(%d,%d)\n", x, y);
ヘッダー(_stdiomm.h
_)は次のとおりです。
_#pragma once
#include <cstdarg>
#include <string>
template <typename T>
std::basic_string<T> format(T const *format, ...);
template <typename T>
std::basic_string<T> vformat(T const *format, va_list args);
_
そしてソース(_stdiomm.cpp
_):
_#include "stdiomm.h"
#include <boost/scoped_array.hpp>
#include <cstdio>
template <>
std::wstring vformat(wchar_t const *format, va_list arguments)
{
#if defined(_WIN32)
int required(_vscwprintf(format, arguments));
assert(required >= 0);
boost::scoped_array<wchar_t> buffer(new wchar_t[required + 1]);
int written(vswprintf(buffer.get(), required + 1, format, arguments));
assert(written == required);
return std::wstring(buffer.get(), written);
#else
# error "No implementation yet"
#endif
}
template <>
std::string vformat(char const *format, va_list arguments)
{
#if defined(_WIN32)
int required(_vscprintf(format, arguments));
assert(required >= 0);
boost::scoped_array<char> buffer(new char[required + 1]);
int written(vsnprintf(buffer.get(), required + 1, format, arguments));
assert(written == required);
return std::string(buffer.get(), written);
#else
char *buffer;
int printed = vasprintf(&buffer, format, arguments);
assert(printed != -1);
std::string retval(buffer, printed);
free(buffer);
return retval;
#endif
}
template <typename T>
std::basic_string<T> format(T const *format, ...)
{
va_list ap;
va_start(ap, format);
std::basic_string<T> retval(vformat(format, ap));
va_end(ap);
return retval;
}
template std::wstring format(wchar_t const *format, ...);
template std::string format(char const *format, ...);
_
他の答えをいくつか読んだ後、 boost::format()
自分に切り替える必要があるかもしれません!
質問はかなり古いものですが、2セントを加算したいと思います。
printf()を使用してユーザーが作成したオブジェクトを印刷する
考えてみると、これはかなり単純です-タイプを文字列化して、printfに文字列を送信できます。
std::string to_string(const MyClass &x)
{
return to_string(x.first)+" "+to_string(x.second);
}
//...
printf("%s is awesome", to_string(my_object).c_str()); //more or less
残念なことはありませんでした(C++ 11 to_string()があります)オブジェクトを文字列化するための標準化されたC++インターフェイス...
printf()pitfall
単一のフラグ-%n
出力パラメーターである唯一のもの-intへのポインターが必要です。正常に書き込まれた文字数を、このポインターが指す場所に書き込みます。これを上手に使用すると、セキュリティの脆弱性であるオーバーランを引き起こす可能性があります(printf()形式文字列攻撃を参照)。
fmt library を使用すると、両方の長所を最大限に活用できます。これは、iostreamの安全性と拡張性と、(s)printf
。例:
std::string = fmt::format("The answer is {}", 42);
このライブラリは、Pythonに似たprintf形式の文字列構文をサポートしています。
免責事項:私は fmt library の著者です。
それは状況次第です。何も完璧ではありません。私は両方を使用します。 ostreamで>>演算子をオーバーロードできるため、ストリームはカスタムタイプに適しています。ただし、間隔などについては、printf()を使用することをお勧めします。 stringstreamなどは、Cスタイルのstrcat()よりも優れています。そのため、状況に適したものを使用してください。
C++streamsは、実際にはオーバーロードされた演算子<<
。
printfはC wayであるため、ストリームはC++ wayであると何度も読みましたが、どちらもライブラリ機能ですC++。したがって、最適なものを使用する必要があります。
私は主にprintfを好みますが、よりクリーンなコードを提供し、プレースホルダーと引数を一致させる必要がないようにするストリームも使用しました。
Coutとcerrはマルチスレッドには安全ではないという警告を読みました。 trueの場合、これを使用しないようにする正当な理由です。注:openMPではGNU g ++を使用します。
ほとんどの場合、一時的なデバッグステートメントにはprintfを使用します。より永続的なコードの場合は、The C++ Wayのように 'c'ストリームを好みます。 boost :: formatは有望に見え、ストリームの使用を置き換える可能性がありますが(特に複雑にフォーマットされた出力の場合)、恐らく長い間printfに代わるものはないでしょう。