C ++ hiện đại làm cho siêu đơn giản này.
C ++ 20
C ++ 20 giới thiệu std::format
, cho phép bạn làm chính xác điều đó. Nó sử dụng các trường thay thế tương tự như trong python :
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
Kiểm tra các tài liệu đầy đủ ! Đó là một cải tiến chất lượng cuộc sống rất lớn.
C ++ 11
Với C ++ 11 s std::snprintf
, điều này đã trở thành một nhiệm vụ khá dễ dàng và an toàn.
#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
}
Đoạn mã trên được cấp phép theo Muff 1.0 .
Từng dòng giải thích:
Mục đích: Viết thành achar*
bằng cách sử dụng std::snprintf
và sau đó chuyển đổi nó thành astd::string
.
Đầu tiên, chúng tôi xác định độ dài mong muốn của mảng char bằng một điều kiện đặc biệt trong snprintf
. Từ cppreference.com :
Giá trị trả về
[...] Nếu chuỗi kết quả bị cắt bớt do giới hạn buf_size, hàm sẽ trả về tổng số ký tự (không bao gồm byte kết thúc null) sẽ được ghi, nếu giới hạn không được áp đặt.
Điều này có nghĩa là kích thước mong muốn là số lượng ký tự cộng với một ký tự , do đó, bộ kết thúc null sẽ ngồi sau tất cả các ký tự khác và nó có thể bị cắt bởi hàm tạo chuỗi. Vấn đề này đã được giải thích bởi @ alexk7 trong các bình luận.
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
sẽ trả về số âm nếu xảy ra lỗi, vì vậy chúng tôi sẽ kiểm tra xem định dạng có hoạt động như mong muốn không. Không làm điều này có thể dẫn đến các lỗi im lặng hoặc phân bổ một bộ đệm lớn, như được chỉ ra bởi @ead trong các bình luận.
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
Tiếp theo, chúng tôi phân bổ một mảng ký tự mới và gán nó cho a std::unique_ptr
. Điều này thường được khuyên, vì bạn sẽ không phải tự làm delete
lại.
Lưu ý rằng đây không phải là cách an toàn để phân bổ một loại unique_ptr
do người dùng xác định vì bạn không thể phân bổ bộ nhớ nếu hàm tạo ném ngoại lệ!
std::unique_ptr<char[]> buf( new char[ size ] );
Sau đó, tất nhiên chúng ta có thể chỉ sử dụng snprintf
cho mục đích sử dụng của nó và viết chuỗi được định dạng vào char[]
.
snprintf( buf.get(), size, format.c_str(), args ... );
Cuối cùng, chúng tôi tạo và trả lại một cái mới std::string
từ đó, đảm bảo bỏ qua bộ kết thúc null ở cuối.
return std::string( buf.get(), buf.get() + size - 1 );
Bạn có thể xem một ví dụ trong hành động ở đây .
Nếu bạn cũng muốn sử dụng std::string
trong danh sách đối số, hãy xem ý chính này .
Thông tin bổ sung cho người dùng Visual Studio :
Như đã giải thích trong câu trả lời này , Microsoft đổi tên std::snprintf
để _snprintf
(có, không std::
). MS tiếp tục đặt nó là không dùng nữa và khuyên nên sử dụng _snprintf_s
thay vào đó, tuy nhiên _snprintf_s
sẽ không chấp nhận bộ đệm bằng 0 hoặc nhỏ hơn đầu ra được định dạng và sẽ không tính toán độ dài đầu ra nếu điều đó xảy ra. Vì vậy, để loại bỏ các cảnh báo khấu hao trong quá trình biên dịch, bạn có thể chèn dòng sau vào đầu tệp có chứa việc sử dụng _snprintf
:
#pragma warning(disable : 4996)
Suy nghĩ cuối cùng
Rất nhiều câu trả lời cho câu hỏi này đã được viết trước thời điểm C ++ 11 và sử dụng độ dài bộ đệm hoặc varg cố định. Trừ khi bạn bị mắc kẹt với các phiên bản cũ của C ++, tôi không khuyên bạn nên sử dụng các giải pháp đó. Lý tưởng nhất là đi theo con đường C ++ 20.
Bởi vì giải pháp C ++ 11 trong câu trả lời này sử dụng các mẫu, nó có thể tạo ra khá nhiều mã nếu nó được sử dụng nhiều. Tuy nhiên, trừ khi bạn đang phát triển cho một môi trường có không gian nhị phân rất hạn chế, đây sẽ không phải là vấn đề và vẫn là một cải tiến lớn so với các giải pháp khác về cả sự rõ ràng và bảo mật.
Nếu hiệu quả không gian là cực kỳ quan trọng, hai giải pháp này với vargs và vsnprintf có thể hữu ích.
KHÔNG SỬ DỤNG bất kỳ giải pháp nào có độ dài bộ đệm cố định, đó chỉ là vấn đề.
boost::format
(như giải pháp của kennytm sử dụng ở đây ).boost::format
cũng đã hỗ trợ các nhà khai thác luồng C ++! ví dụ :cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
có ít dòng mã nhất ... được đánh giá ngang hàng và tích hợp độc đáo với các luồng C ++.