C ++ modern membuat ini sangat sederhana.
C ++ 20
C ++ 20 memperkenalkan std::format
, yang memungkinkan Anda untuk melakukan hal itu. Ini menggunakan bidang pengganti yang mirip dengan yang ada di python :
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
Lihat dokumentasi lengkapnya ! Ini merupakan peningkatan kualitas hidup yang luar biasa.
C ++ 11
Dengan C ++ 11 s std::snprintf
, ini sudah menjadi tugas yang cukup mudah dan aman.
#include <memory>
#include <string>
#include <stdexcept>
template<typename ... Args>
std::string string_format( const std::string& format, Args ... args )
{
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
std::unique_ptr<char[]> buf( new char[ size ] );
snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
Cuplikan kode di atas dilisensikan di bawah CC0 1.0 .
Penjelasan baris demi baris:
Tujuan: Menulis ke achar*
dengan menggunakan std::snprintf
lalu mengonversinya menjadi astd::string
.
Pertama, kami menentukan panjang array char yang diinginkan menggunakan kondisi khusus di snprintf
. Dari cppreference.com :
Nilai pengembalian
[...] Jika string yang dihasilkan terpotong karena batas buf_size, fungsi mengembalikan jumlah karakter (tidak termasuk terminasi null-byte) yang seharusnya ditulis, jika batas itu tidak dikenakan.
Ini berarti bahwa ukuran yang diinginkan adalah jumlah karakter ditambah satu , sehingga null-terminator akan duduk setelah semua karakter lain dan dapat dipotong oleh konstruktor string lagi. Masalah ini dijelaskan oleh @ alexk7 di komentar.
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
akan mengembalikan angka negatif jika terjadi kesalahan, jadi kami kemudian memeriksa apakah pemformatan berfungsi sesuai keinginan. Tidak melakukan ini dapat menyebabkan kesalahan diam atau alokasi buffer besar, seperti yang ditunjukkan oleh @ead di komentar.
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
Selanjutnya, kami mengalokasikan array karakter baru dan menetapkannya ke a std::unique_ptr
. Ini umumnya disarankan, karena Anda tidak perlu melakukannya delete
lagi secara manual .
Perhatikan bahwa ini bukan cara yang aman untuk mengalokasikan unique_ptr
tipe yang ditentukan pengguna karena Anda tidak dapat membatalkan alokasi memori jika konstruktor melempar pengecualian!
std::unique_ptr<char[]> buf( new char[ size ] );
Setelah itu, kita tentu saja bisa menggunakan snprintf
untuk tujuan penggunaannya dan menulis string yang diformat ke char[]
.
snprintf( buf.get(), size, format.c_str(), args ... );
Akhirnya, kami membuat dan mengembalikan yang baru std::string
dari itu, memastikan untuk menghilangkan null-terminator di akhir.
return std::string( buf.get(), buf.get() + size - 1 );
Anda dapat melihat contoh beraksi di sini .
Jika Anda juga ingin menggunakan std::string
dalam daftar argumen, lihat intisari ini .
Informasi tambahan untuk pengguna Visual Studio :
Sebagaimana dijelaskan dalam jawaban ini , Microsoft berganti nama std::snprintf
menjadi _snprintf
(ya, tanpa std::
). MS selanjutnya menetapkannya sebagai usang dan menyarankan untuk menggunakannya _snprintf_s
sebagai gantinya, namun _snprintf_s
tidak akan menerima buffer menjadi nol atau lebih kecil dari output yang diformat dan tidak akan menghitung panjang output jika itu terjadi. Jadi untuk menghilangkan peringatan penghentian selama kompilasi, Anda dapat memasukkan baris berikut di bagian atas file yang berisi penggunaan _snprintf
:
#pragma warning(disable : 4996)
Pikiran terakhir
Banyak jawaban untuk pertanyaan ini ditulis sebelum waktu C ++ 11 dan menggunakan panjang buffer tetap atau vargs. Kecuali Anda terjebak dengan versi lama C ++, saya tidak akan merekomendasikan menggunakan solusi tersebut. Idealnya, gunakan cara C ++ 20.
Karena solusi C ++ 11 dalam jawaban ini menggunakan templat, ini dapat menghasilkan sedikit kode jika banyak digunakan. Namun, kecuali Anda mengembangkan untuk lingkungan dengan ruang biner yang sangat terbatas, ini tidak akan menjadi masalah dan masih merupakan peningkatan besar atas solusi lain dalam kejelasan dan keamanan.
Jika efisiensi ruang sangat penting, dua solusi dengan vargs dan vsnprintf ini dapat bermanfaat.
JANGAN GUNAKAN solusi apa pun dengan panjang buffer tetap, yang hanya meminta masalah.
boost::format
(seperti solusi kennytm gunakan di sini ).boost::format
sudah mendukung operator stream C ++ juga! Contoh:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
memiliki paling sedikit baris kode ... ditinjau oleh rekan dan terintegrasi dengan baik dengan aliran C ++.