Modern C ++ bunu süper basitleştirir.
C ++ 20
C ++ 20std::format
, tam olarak bunu yapmanızı sağlayan tanıtır . Bu benzer yedek alanlarını kullanan piton olanlar :
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
Tüm belgelere göz atın ! Büyük bir yaşam kalitesi iyileştirmesi.
C ++ 11
İle C ++ 11 s std::snprintf
, bu zaten oldukça kolay ve güvenli bir görev haline geldi.
#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
}
Yukarıdaki kod snippet'i CC0 1.0 altında lisanslanmıştır .
Satır satır açıklama:
Amaç: achar*
tuşunu kullanarak yazınstd::snprintf
ve ardından a'ya dönüştürünstd::string
.
İlk olarak, içinde özel bir koşul kullanarak char dizisinin istenen uzunluğunu belirleriz snprintf
. Gönderen cppreference.com :
Geri dönüş değeri
[...] Sonuçta elde edilen dize buf_size sınırı nedeniyle kesilirse, işlev, sınır verilmemişse, yazılacak toplam karakter sayısını (sonlanan boş bayt dahil değil) döndürür.
Bu, istenen boyutun karakter sayısı artı bir olduğu anlamına gelir , böylece boş sonlandırıcı diğer tüm karakterlerin arkasına oturacak ve dize oluşturucu tarafından tekrar kesilebilecektir. Bu sorun yorumlarda @ alexk7 tarafından açıklanmıştır.
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
bir hata oluşursa negatif bir sayı döndürür, bu nedenle biçimlendirmenin istendiği gibi çalışıp çalışmadığını kontrol ederiz. Bunu yapmamak, yorumlarda @ead'in belirttiği gibi sessiz hatalara veya büyük bir arabellek tahsisine yol açabilir.
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
Ardından, yeni bir karakter dizisi atarız ve onu a'ya atarız std::unique_ptr
. delete
Bunu tekrar manuel olarak yapmak zorunda kalmayacağınız için genellikle tavsiye edilir .
Bunun, unique_ptr
yapıcı bir istisna atarsa belleği ayıramayacağınız için kullanıcı tanımlı türlerle ayırmanın güvenli bir yolu olmadığını unutmayın !
std::unique_ptr<char[]> buf( new char[ size ] );
Bundan sonra, elbette sadece snprintf
amaçlanan kullanımı için kullanabilir ve biçimlendirilmiş dizeyi char[]
.
snprintf( buf.get(), size, format.c_str(), args ... );
Son olarak, std::string
sonunda boş-sonlandırıcıyı atladığınızdan emin olarak yeni bir tane oluşturup iade ediyoruz .
return std::string( buf.get(), buf.get() + size - 1 );
Burada bir örnek görebilirsiniz .
std::string
Argüman listesinde de kullanmak istiyorsanız , bu özete bir göz atın .
Visual Studio kullanıcıları için ek bilgiler :
Açıklandığı gibi bu cevap Microsoft değiştirildi std::snprintf
etmek _snprintf
(evet, olmadan std::
). MS ayrıca kullanımdan kaldırılmıştır ve _snprintf_s
bunun yerine kullanılmasını önerir , ancak _snprintf_s
tamponun biçimlendirilmiş çıktıdan sıfır veya daha küçük olduğunu kabul etmez ve bu durumda çıktıların uzunluğunu hesaplamaz. Bu nedenle derleme sırasında kullanımdan kaldırma uyarılarından kurtulmak için dosyanın üst kısmına aşağıdakilerin kullanımını içeren aşağıdaki satırı ekleyebilirsiniz _snprintf
:
#pragma warning(disable : 4996)
Son düşünceler
Bu soruya birçok cevap C ++ 11 zamanından önce yazılmıştır ve sabit tampon uzunlukları veya değişkenleri kullanılmıştır. C ++ 'ın eski sürümlerine bağlı kalmadıkça, bu çözümleri kullanmanızı tavsiye etmem. İdeal olarak, C ++ 20 yoluna gidin.
Bu yanıttaki C ++ 11 çözümü şablonlar kullandığından, çok kullanılırsa biraz kod üretebilir. Bununla birlikte, ikili dosyalar için çok sınırlı alana sahip bir ortam için geliştirmediğiniz sürece, bu bir sorun olmayacak ve hala hem açıklık hem de güvenlikteki diğer çözümlere göre büyük bir gelişme olacaktır.
Alan verimliliği çok önemliyse, değişkenler ve vsnprintf içeren bu iki çözüm yararlı olabilir.
Sadece sorun isteyen sabit tampon uzunluklarına sahip herhangi bir çözüm KULLANMAYIN .
boost::format
(kennytm'in çözümü burada kullandığı için ).boost::format
zaten C ++ akış operatörlerini de destekliyor! Örnek:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
en az kod satırına sahiptir ... hakemli ve C ++ akışları ile güzel bir şekilde bütünleşir.