std::sprintf와 같은 문자열 형식
포맷해서 파일 스트림으로 보내야 합니다.어떻게 해야 하나요?
현대의 C++은 이것을 매우 단순하게 만듭니다.
C++20
C++20은 바로 이를 가능하게 해주는 를 소개합니다.python의 필드와 유사한 대체 필드를 사용합니다.
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
cppreference.com , CC BY-SA 및 GFDL의 코드
컴파일러 지원 페이지에서 표준 라이브러리 구현에서 사용할 수 있는지 확인하십시오.2021-11-28 현재 2021-05-25에 출시된 Visual Studio 2019 16.10과 여기서 추적되는 Clang 14에서 부분 지원이 가능합니다.다른 모든 경우 아래의 C++11 솔루션을 사용하거나 다음과 같은 의미를 갖는 라이브러리를 사용할 수 있습니다.std::format
.
C++11
C++11을 사용하면 이것은 이미 꽤 쉽고 안전한 작업이 되었습니다.
#include <memory>
#include <string>
#include <stdexcept>
template<typename ... Args>
std::string string_format( const std::string& format, Args ... args )
{
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
auto size = static_cast<size_t>( size_s );
std::unique_ptr<char[]> buf( new char[ size ] );
std::snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
위의 코드 조각은 CC01.0에 따라 라이센스가 부여됩니다.
한 줄 한 줄 설명
목표: 다음에 쓰기char*
을 이용하여std::snprintf
그리고 나서 그것을 a로 변환합니다.std::string
.
먼저, 우리는 다음과 같은 특별한 조건을 사용하여 원하는 문자 배열의 길이를 결정합니다.snprintf
cppreference.com 에서:
반환값
[...] buf_size 제한으로 인해 결과 문자열이 잘린 경우 함수는 제한이 적용되지 않은 경우 작성되었을 총 문자 수(종료 null-byte 제외)를 반환합니다.
즉, 원하는 크기는 문자 수 + 1이므로 null-terminator는 다른 모든 문자 뒤에 배치되고 문자열 생성자에 의해 다시 차단될 수 있습니다.이 문제는 댓글에 @alexk7에 의해 설명되었습니다.
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
오류가 발생하면 음수를 반환하므로 포맷이 원하는 대로 작동했는지 확인합니다.이렇게 하지 않으면 @ead가 댓글에서 지적한 것처럼 무음 오류가 발생하거나 큰 버퍼가 할당될 수 있습니다.
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
왜냐하면 우리는 알고 있기 때문입니다.size_s
수 . 된 부적일없수에서 합니다. 우리는 서명된 것에서 변환하기 위해 정적 캐스트를 사용합니다.int
없는 .size_t
이런 식으로, 가장 현학적인 컴파일러도 그렇지 않으면 다음 행에서 발생할 변환에 대해 불평하지 않을 것입니다.
size_t size = static_cast<size_t>( size_s );
는 새로운 배열을 을 다으로새, 우는로문배자할열당다할그니다당합음에 할당합니다.std::unique_ptr
수동으로 다시 삭제할 필요가 없으므로 일반적으로 이 방법을 사용하는 것이 좋습니다.
은 것은안할수당있방아다닙니법이는할게이전하▁a▁way다▁▁to▁safe▁note▁is니아닙▁a를 할당하는 안전한 방법이 아닙니다.unique_ptr
생성자가 예외를 발생시킬 경우 메모리 할당을 해제할 수 없으므로 사용자 정의 유형을 사용합니다!
std::unique_ptr<char[]> buf( new char[ size ] );
C++14에서는 사용자 정의 유형에 안전한 를 대신 사용할 수 있습니다.
auto buf = std::make_unique<char[]>( size );
그 후에, 우리는 물론 그냥 사용할 수 있습니다.snprintf
을 도된용도사포문맷자다씁음니다에을열된의용고로 씁니다.char[]
.
std::snprintf( buf.get(), size, format.c_str(), args ... );
마지막으로, 우리는 새로운 것을 만들고 반환합니다.std::string
여기서 끝에 있는 null-terminator를 생략합니다.
return std::string( buf.get(), buf.get() + size - 1 );
여기서 실행 중인 예를 볼 수 있습니다.
당신도 는경우려하용을 사용하고 싶다면,std::string
논쟁 목록에서, 이 요점을 살펴보세요.
이 답변에서 설명한 바와 같이 Microsoft는 이름을 변경했습니다.std::snprintf
_snprintf
, 없음)std::
MS는 더 이상 사용하지 않는 것으로 설정하고 대신 사용할 것을 권장합니다._snprintf_s
버퍼가 포맷된 출력보다 작거나 0인 경우 이를 허용하지 않으며 출력 길이가 계산되지 않습니다.따라서 컴파일 중에 더 이상 사용되지 않는 경고를 제거하기 위해 파일의 맨 위에 다음 행을 삽입할 수 있습니다._snprintf
:
#pragma warning(disable : 4996)
마지막 생각
이 질문에 대한 많은 답변은 C++11 이전에 작성되었으며 고정 버퍼 길이 또는 변수를 사용합니다.C++의 이전 버전에 얽매이지 않는 한, 저는 이러한 솔루션을 사용하는 것을 추천하지 않습니다.이상적으로는 C++20 방식을 사용합니다.
이 답변의 C++11 솔루션은 템플릿을 사용하기 때문에 많이 사용하면 상당한 코드를 생성할 수 있습니다.그러나 바이너리를 위한 공간이 매우 제한된 환경을 위해 개발하는 경우가 아니라면 이는 문제가 되지 않으며 명확성과 보안 측면에서 여전히 다른 솔루션에 비해 크게 개선된 것입니다.
공간 효율성이 매우 중요한 경우 vargs와 vsnprintf를 사용하는 이 두 가지 솔루션이 유용할 수 있습니다. 버퍼 길이가 고정된 솔루션은 사용하지 마십시오. 문제가 발생할 수 있습니다.
기본 버퍼에 대한 쓰기 액세스 권한이 없기 때문에 직접 할 수 없습니다(C++11까지; Dietrich Epp의 코멘트 참조).먼저 c-string으로 한 다음 std:: string으로 복사해야 합니다.
char buff[100];
snprintf(buff, sizeof(buff), "%s", "Hello");
std::string buffAsStdStr = buff;
그런데 왜 그냥 스트링 스트림을 사용하지 않으셨는지 모르겠네요.당신이 이렇게 하는 것만이 아닌 구체적인 이유가 있다고 생각합니다.
std::ostringstream stringStream;
stringStream << "Hello";
std::string copyOfStr = stringStream.str();
C++11을 사용한 Cvsnprintf()
내부:
#include <stdarg.h> // For va_start, etc.
std::string string_format(const std::string fmt, ...) {
int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code
std::string str;
va_list ap;
while (1) { // Maximum two passes on a POSIX system...
str.resize(size);
va_start(ap, fmt);
int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
va_end(ap);
if (n > -1 && n < size) { // Everything worked
str.resize(n);
return str;
}
if (n > -1) // Needed size returned
size = n + 1; // For null char
else
size *= 2; // Guess at a larger size (OS specific)
}
return str;
}
더 안전하고 효율적인 접근 방식(테스트를 해보니 더 빠름):
#include <stdarg.h> // For va_start, etc.
#include <memory> // For std::unique_ptr
std::string string_format(const std::string fmt_str, ...) {
int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
std::unique_ptr<char[]> formatted;
va_list ap;
while(1) {
formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
strcpy(&formatted[0], fmt_str.c_str());
va_start(ap, fmt_str);
final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
va_end(ap);
if (final_n < 0 || final_n >= n)
n += abs(final_n - n + 1);
else
break;
}
return std::string(formatted.get());
}
그fmt_str
는 의 요 사 항 위 값 전 의 됩 니 달 다 해 에 해 구 준 기 수 하 을 ments ▁to ▁of ▁is ▁require ▁passed ▁by ▁conform 니 ▁with 다 됩 ▁value ▁the 달 전 의 의va_start
.
참고: 일부 시스템에서는 "안전한" 버전과 "빠른" 버전이 작동하지 않습니다.따라서 둘 다 여전히 나열됩니다. "더 빠른"은 사전 , "더 빠른" 것, "더 빠른" 것, "더 빠른" 것입니다. 그렇지 않으면,strcpy
속도가 느려집니다.
C++20은 다음과 유사합니다.sprintf
API 측면에서는 완전히 안전하지만 사용자 정의 유형과 함께 작동하며 Python과 유사한 형식 문자열 구문을 사용합니다.포맷방다같다습니음과은법다같니습을 포맷하는 방법은 std::string
스트림에 기록합니다.
std::string s = "foo";
std::cout << std::format("Look, a string: {}", s);
또는 {fmt} 라이브러리를 사용하여 문자열을 포맷하고 다음에 쓸 수 있습니다.stdout
또는 파일 스트림을 한 번에 실행할 수 있습니다.
fmt::print("Look, a string: {}", s);
에 대해서는sprintf
여기에 있는 의 또는여있다답대른불변부행 GCC의 기와같변과 같은 한 하지 않습니다.format
리터럴 형식 문자열에서만 작동하는 특성입니다.다음 예제에서는 이러한 기능이 안전하지 않은 이유를 확인할 수 있습니다.
std::string format_str = "%s";
string_format(format_str, format_str[0]);
string_format
에릭 아론스티의 답변에서 구현한 것입니다.하려고 할 때 .
$ g++ -Wall -Wextra -pedantic test.cc
$ ./a.out
Segmentation fault: 11
고지 사항:저는 {fmt}와 C++20의 저자입니다.std::format
.
boost::format()
원하는 기능을 제공합니다.
부스트 형식 라이브러리 시놉시스:
형식 개체는 형식 문자열로 구성된 다음 연산자 %에 대한 반복 호출을 통해 인수가 지정됩니다.그런 다음 각 인수는 문자열로 변환되며 형식 문자열에 따라 하나의 문자열로 결합됩니다.
#include <boost/format.hpp>
cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
// prints "writing toto, x=40.230 : 50-th try"
테스트를 거친 프로덕션 품질 답변
이 답변은 표준 준수 기술을 사용하여 일반적인 사례를 참조하십시오.동일한 접근 방식이 페이지 하단 근처에 있는 CppReference.com 의 예로 제시되어 있습니다.그들의 예와 달리, 이 코드는 질문의 요구 사항에 맞고 로봇 공학 및 위성 응용 분야에서 현장 테스트를 거칩니다.주석 기능도 향상되었습니다.설계 품질에 대해서는 아래에서 자세히 설명합니다.
#include <string>
#include <cstdarg>
#include <vector>
// requires at least C++11
const std::string vformat(const char * const zcFormat, ...) {
// initialize use of the variable argument array
va_list vaArgs;
va_start(vaArgs, zcFormat);
// reliably acquire the size
// from a copy of the variable argument array
// and a functionally reliable call to mock the formatting
va_list vaArgsCopy;
va_copy(vaArgsCopy, vaArgs);
const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
va_end(vaArgsCopy);
// return a formatted string without risking memory mismanagement
// and without assuming any compiler or platform specific behavior
std::vector<char> zc(iLen + 1);
std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
va_end(vaArgs);
return std::string(zc.data(), iLen); }
#include <ctime>
#include <iostream>
#include <iomanip>
// demonstration of use
int main() {
std::time_t t = std::time(nullptr);
std::cerr
<< std::put_time(std::localtime(& t), "%D %T")
<< " [debug]: "
<< vformat("Int 1 is %d, Int 2 is %d, Int 3 is %d", 11, 22, 33)
<< std::endl;
return 0; }
예측 가능한 선형 효율성
질문 사양에 따라 안전하고 신뢰할 수 있으며 예측 가능한 재사용 가능한 기능을 사용하려면 두 개의 패스가 필요합니다.재사용 가능한 기능에서 변수의 크기 분포에 대한 가정은 프로그래밍 스타일이 나쁘므로 피해야 합니다.이 경우, 임의로 큰 변수의 길이 표현은 알고리즘 선택의 핵심 요소입니다.
오버플로 시 재시도는 기하급수적으로 비효율적이며, 이는 쓰기 버퍼가 null일 때 모의 실행을 제공하기 위해 C++11 표준 위원회가 위의 제안을 논의했을 때 논의된 또 다른 이유입니다.
위의 프로덕션 준비 구현에서 첫 번째 실행은 할당 크기를 결정하기 위한 모의 실행입니다.할당이 발생하지 않습니다.printf 지시어의 구문 분석과 vargs의 판독은 수십 년에 걸쳐 매우 효율적으로 이루어졌습니다.사소한 경우에 작은 비효율성을 희생하더라도 재사용 가능한 코드는 예측 가능해야 합니다.
보안 및 안정성
Andrew Koenig는 캠브리지 행사에서 강연을 한 후 우리의 작은 그룹에게 "사용자 기능은 예외적인 기능을 위해 실패의 악용에 의존해서는 안 됩니다."라고 말했습니다.여느 때처럼, 그의 지혜는 그 이후로 기록에서 진실로 나타났습니다.수정되거나 닫힌 보안 버그 문제는 수정 전에 악용된 구멍에 대한 설명에서 재시도 해킹을 나타내는 경우가 많습니다.
이것은 sprintf에 대한 대안, C9X 개정 제안서, ISO IEC 문서 WG14 N645/X3J1196-008의 null 버퍼 기능에 대한 공식 표준 개정 제안서에 언급되어 있습니다.동적 메모리 가용성의 제약 조건 내에서 인쇄 지시어별로 삽입된 임의의 긴 문자열 "%s"도 예외가 아니며 "예외적이지 않은 기능"을 생성하기 위해 이용해서는 안 됩니다.
이 답변의 첫 번째 단락에 연결된 C++Reference.org 페이지 하단에 제공된 예제 코드와 함께 제안을 고려합니다.
또한 실패 사례의 테스트는 성공 사례만큼 강력한 경우가 거의 없습니다.
휴대성
모든 주요 OS 벤더는 c++11 표준의 일부로 std::vsnprintf를 완벽하게 지원하는 컴파일러를 제공합니다.더 이상 배포를 유지하지 않는 공급업체의 제품을 실행하는 호스트는 여러 가지 이유로 g++ 또는 clang++로 제공되어야 합니다.
스택 사용
std::vsnprintf에 대한 첫 번째 호출에서의 스택 사용은 두 번째 호출보다 작거나 같을 것이며, 두 번째 호출이 시작되기 전에 해제될 것입니다.첫 번째 호출이 스택 가용성을 초과하면 std::fprintf도 실패합니다.
나는 vsnprintf를 사용하여 나만의 버퍼를 생성하지 않고 문자열을 반환하도록 나만의 버퍼를 작성했습니다.
#include <string>
#include <cstdarg>
//missing string printf
//this is safe and convenient but not exactly efficient
inline std::string format(const char* fmt, ...){
int size = 512;
char* buffer = 0;
buffer = new char[size];
va_list vl;
va_start(vl, fmt);
int nsize = vsnprintf(buffer, size, fmt, vl);
if(size<=nsize){ //fail delete buffer and try again
delete[] buffer;
buffer = 0;
buffer = new char[nsize+1]; //+1 for /0
nsize = vsnprintf(buffer, size, fmt, vl);
}
std::string ret(buffer);
va_end(vl);
delete[] buffer;
return ret;
}
이렇게 사용하시면 됩니다.
std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5);
포려면의 std::string
swf으로 'swf' , 출호sprint라고 .snprintf
)로 표시됩니다.nullptr
그리고.0
길이를 . 필요한 버퍼 길이를 가져옵니다.다음과 같이 C++11 변수 템플릿을 사용하여 함수를 작성합니다.
#include <cstdio>
#include <string>
#include <cassert>
template< typename... Args >
std::string string_sprintf( const char* format, Args... args ) {
int length = std::snprintf( nullptr, 0, format, args... );
assert( length >= 0 );
char* buf = new char[length + 1];
std::snprintf( buf, length + 1, format, args... );
std::string str( buf );
delete[] buf;
return str;
}
를 들어,C++ 예를 들어, GCC: C11은 다음과 같습니다.g++ -std=c++11
용도:
std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);
C++20std::format
도착했습니다!이 기능은 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html 에 설명되어 있으며 Python과 유사한 기능을 사용합니다..format()
통사론
용도는 다음과 같습니다.
#include <format>
#include <string>
int main() {
std::string message = std::format("The answer is {}.", 42);
}
9 및 GCC 9.1.0 »g++-9 -std=c++2a
여전히 그것을 지원하지 않습니다.
의 기의존.fmt
라이브러리는 공식적인 지원을 받기 전에 이를 구현합니다. https://github.com/fmtlib/fmt 은 이전에 언급한 대로: std:: string formating(예: sprintf Install on Ubuntu 22.04: Ubuntu 22.04:
sudo apt install libfmt-dev
바꿀 소스 수정:
<format>
와 함께<fmt/core.h>
std::format
fmt::format
main.cpp
#include <string>
#include <iostream>
#include <fmt/core.h>
int main() {
std::string message = fmt::format("The answer is {}.", 42);
std::cout << message << std::endl;
}
컴파일 및 실행 방법:
g++ -std=c++11 -o main.out main.cpp -lfmt
./main.out
출력:
The answer is 42.
API를 합니다.std::format
머리글:
" " " " " " " " " " 에 되어 있습니다.
<format>
기존 코드에 영향을 미치지 않아야 합니다.
형식 16진수{:x}
영점 행선점영{:03}
맞춤 쪽왼 정렬정{:<}
요, 그럼요.{:>}
,점,점{:^}
정밀도 측정기{:.2}
합니다.{:+}
을 음으로부표로 합니다.true
그리고.false
:{:}
직접 printf를 호출하지 않고 printf와 유사한 구문만 원하는 경우 Boost Format을 확인하십시오.
template<typename... Args>
std::string string_format(const char* fmt, Args... args)
{
size_t size = snprintf(nullptr, 0, fmt, args...);
std::string buf;
buf.reserve(size + 1);
buf.resize(size);
snprintf(&buf[0], size + 1, fmt, args...);
return buf;
}
C99 snprintf 및 C++11 사용
[편집: 20/05/25] 더 나은...:
머리글 입력:
// `say` prints the values
// `says` returns a string instead of printing
// `sayss` appends the values to it's first argument instead of printing
// `sayerr` prints the values and returns `false` (useful for return statement fail-report)<br/>
void PRINTSTRING(const std::string &s); //cater for GUI, terminal, whatever..
template<typename...P> void say(P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); PRINTSTRING(r); }
template<typename...P> std::string says(P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); return r; }
template<typename...P> void sayss(std::string &s, P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); s+=r; } //APPENDS! to s!
template<typename...P> bool sayerr(P...p) { std::string r{}; std::stringstream ss("ERROR: "); (ss<<...<<p); r=ss.str(); PRINTSTRING(r); return false; }
그PRINTSTRING(r)
- 기능은 다음을 사용하여 GUI 또는 터미널 또는 특수 출력 요구 사항을 충족하는 것입니다.#ifdef _some_flag_
기본값은 다음과 같습니다.
void PRINTSTRING(const std::string &s) { std::cout << s << std::flush; }
[edit '17/8/31] 변수 템플릿 버전 'vtspf(...)' 추가:
template<typename T> const std::string type_to_string(const T &v)
{
std::ostringstream ss;
ss << v;
return ss.str();
};
template<typename T> const T string_to_type(const std::string &str)
{
std::istringstream ss(str);
T ret;
ss >> ret;
return ret;
};
template<typename...P> void vtspf_priv(std::string &s) {}
template<typename H, typename...P> void vtspf_priv(std::string &s, H h, P...p)
{
s+=type_to_string(h);
vtspf_priv(s, p...);
}
template<typename...P> std::string temp_vtspf(P...p)
{
std::string s("");
vtspf_priv(s, p...);
return s;
}
그것은 때때로 방해가 되는 것의 효과적인 쉼표 버전입니다.<<
slot, 다음같사니다용됩이.
char chSpace=' ';
double pi=3.1415;
std::string sWorld="World", str_var;
str_var = vtspf("Hello", ',', chSpace, sWorld, ", pi=", pi);
[edit] Erik Aronesty의 답변(위)에서 기술을 사용하도록 조정되었습니다.
#include <string>
#include <cstdarg>
#include <cstdio>
//=============================================================================
void spf(std::string &s, const std::string fmt, ...)
{
int n, size=100;
bool b=false;
va_list marker;
while (!b)
{
s.resize(size);
va_start(marker, fmt);
n = vsnprintf((char*)s.c_str(), size, fmt.c_str(), marker);
va_end(marker);
if ((n>0) && ((b=(n<size))==true)) s.resize(n); else size*=2;
}
}
//=============================================================================
void spfa(std::string &s, const std::string fmt, ...)
{
std::string ss;
int n, size=100;
bool b=false;
va_list marker;
while (!b)
{
ss.resize(size);
va_start(marker, fmt);
n = vsnprintf((char*)ss.c_str(), size, fmt.c_str(), marker);
va_end(marker);
if ((n>0) && ((b=(n<size))==true)) ss.resize(n); else size*=2;
}
s += ss;
}
답이전 답]
매우 늦은 답변이지만, 저처럼 'sprintf' 방식을 좋아하는 사람들을 위해, 저는 다음과 같은 기능을 쓰고 사용하고 있습니다.마음에 들면, 스프린트 폰에 더 가깝게 %-옵션을 확장할 수 있습니다. 현재 그 옵션은 제가 필요로 하는 것으로 충분합니다.sprintf와 합니다. stringf() stringfappend()는 stringf.sprintf와 동일합니다.에 대한 유형이어야 에 대한 매개 변수는 POD 유형이어야 합니다.
//=============================================================================
void DoFormatting(std::string& sF, const char* sformat, va_list marker)
{
char *s, ch=0;
int n, i=0, m;
long l;
double d;
std::string sf = sformat;
std::stringstream ss;
m = sf.length();
while (i<m)
{
ch = sf.at(i);
if (ch == '%')
{
i++;
if (i<m)
{
ch = sf.at(i);
switch(ch)
{
case 's': { s = va_arg(marker, char*); ss << s; } break;
case 'c': { n = va_arg(marker, int); ss << (char)n; } break;
case 'd': { n = va_arg(marker, int); ss << (int)n; } break;
case 'l': { l = va_arg(marker, long); ss << (long)l; } break;
case 'f': { d = va_arg(marker, double); ss << (float)d; } break;
case 'e': { d = va_arg(marker, double); ss << (double)d; } break;
case 'X':
case 'x':
{
if (++i<m)
{
ss << std::hex << std::setiosflags (std::ios_base::showbase);
if (ch == 'X') ss << std::setiosflags (std::ios_base::uppercase);
char ch2 = sf.at(i);
if (ch2 == 'c') { n = va_arg(marker, int); ss << std::hex << (char)n; }
else if (ch2 == 'd') { n = va_arg(marker, int); ss << std::hex << (int)n; }
else if (ch2 == 'l') { l = va_arg(marker, long); ss << std::hex << (long)l; }
else ss << '%' << ch << ch2;
ss << std::resetiosflags (std::ios_base::showbase | std::ios_base::uppercase) << std::dec;
}
} break;
case '%': { ss << '%'; } break;
default:
{
ss << "%" << ch;
//i = m; //get out of loop
}
}
}
}
else ss << ch;
i++;
}
va_end(marker);
sF = ss.str();
}
//=============================================================================
void stringf(string& stgt,const char *sformat, ... )
{
va_list marker;
va_start(marker, sformat);
DoFormatting(stgt, sformat, marker);
}
//=============================================================================
void stringfappend(string& stgt,const char *sformat, ... )
{
string sF = "";
va_list marker;
va_start(marker, sformat);
DoFormatting(sF, sformat, marker);
stgt += sF;
}
Google은 다음과 같이 합니다. (BSD 라이센스)
그리고 페이스북은 꽤 비슷한 방식으로 그것을 합니다: (Apache License)
다 한 둘다편을제다니공합함리▁를 제공합니다.StringAppendF
도 마찬가지야
이 매우 인기 있는 질문에 대해 제 의견을 말씀드리겠습니다.
성공적으로 반환되면 이러한 함수는 출력을 문자열로 끝내는 데 사용되는 null 바이트를 제외하고 인쇄된 문자 수를 반환합니다.
snprintf() 및 vsnprintf() 함수는 크기 바이트(종료 null 바이트('\0')를 초과하여 쓰지 않습니다.이 제한으로 인해 출력이 잘린 경우 반환 값은 사용 가능한 공간이 충분했다면 최종 문자열에 기록되었을 문자 수(종료 null 바이트 제외)입니다.따라서 반환 값이 크기 이상이면 출력이 잘렸다는 것을 의미합니다.
다시 말해, 건전한 C++11 구현은 다음과 같아야 합니다.
#include <string>
#include <cstdio>
template <typename... Ts>
std::string fmt (const std::string &fmt, Ts... vs)
{
char b;
size_t required = std::snprintf(&b, 0, fmt.c_str(), vs...) + 1;
// See comments: the +1 is necessary, while the first parameter
// can also be set to nullptr
char bytes[required];
std::snprintf(bytes, required, fmt.c_str(), vs...);
return std::string(bytes);
}
꽤 잘 작동합니다 :)
가변 템플릿은 C++11에서만 지원됩니다.픽셀 포인트의 답변은 이전 프로그래밍 스타일을 사용하는 유사한 기술을 보여줍니다.
C++에 그런 것이 없다는 것이 이상합니다.그들은 최근에 덧붙였는데, 제 생각에 그것은 큰 진전입니다.저는 그들이 a를 추가할지 궁금합니다..format
를 의교환으로 바꿉니다.std::string
결국엔...
편집
했듯이, Alexk7은 A.+1
는 의 반 값 필 합니요다의 합니다.std::snprintf
우리는 그것을 위한 공간이 필요하기 때문에.\0
에서 이트가 되었습니다. 직관적으로 대부분의 아키텍처에서 누락된+1
원인이 될 것입니다.required
는 " " " " 입니다.0
이는 평가 후에 발생합니다.required
변수로의 실제 std::snprintf
따라서 효과가 보이지 않아야 합니다.
컴파일러 예로 , 이바뀔 수 : 한다면?required
변수? 하는 종류의 오류입니다.이러한 오류로 인해 보안 문제가 발생하기도 합니다.
sprintf(3)가 있는 시스템에 있는 경우 다음과 같이 쉽게 래핑할 수 있습니다.
#include <iostream>
#include <cstdarg>
#include <cstdio>
std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
std::string format(const char *fmt, ...)
{
std::string result;
va_list ap;
va_start(ap, fmt);
char *tmp = 0;
int res = vasprintf(&tmp, fmt, ap);
va_end(ap);
if (res != -1) {
result = tmp;
free(tmp);
} else {
// The vasprintf call failed, either do nothing and
// fall through (will return empty string) or
// throw an exception, if your code uses those
}
return result;
}
int main(int argc, char *argv[]) {
std::string username = "you";
std::cout << format("Hello %s! %d", username.c_str(), 123) << std::endl;
return 0;
}
Erik Aronesty가 제공한 답변을 기반으로 합니다.
std::string string_format(const std::string &fmt, ...) {
std::vector<char> str(100,'\0');
va_list ap;
while (1) {
va_start(ap, fmt);
auto n = vsnprintf(str.data(), str.size(), fmt.c_str(), ap);
va_end(ap);
if ((n > -1) && (size_t(n) < str.size())) {
return str.data();
}
if (n > -1)
str.resize( n + 1 );
else
str.resize( str.size() * 2);
}
return str.data();
}
이렇게 하면 버려질 필요가 없습니다.const
의 .c_str()
그것은 원래 대답에 있었습니다.
inline void format(string& a_string, const char* fmt, ...)
{
va_list vl;
va_start(vl, fmt);
int size = _vscprintf( fmt, vl );
a_string.resize( ++size );
vsnprintf_s((char*)a_string.data(), size, _TRUNCATE, fmt, vl);
va_end(vl);
}
string에는 필요한 것이 없지만 std:: stringstream에는 있습니다.문자열 스트림을 사용하여 문자열을 만든 다음 문자열을 추출합니다.여기 당신이 할 수 있는 것들에 대한 포괄적인 목록이 있습니다.예:
cout.setprecision(10); //stringstream is a stream like cout
더블 또는 플로트 인쇄 시 소수점 10자리의 정밀도를 제공합니다.
사용해 볼 수 있습니다.
string str;
str.resize( _MAX_PATH );
sprintf( &str[0], "%s %s", "hello", "world" );
// optionals
// sprintf_s( &str[0], str.length(), "%s %s", "hello", "world" ); // Microsoft
// #include <stdio.h>
// snprintf( &str[0], str.length(), "%s %s", "hello", "world" ); // c++11
str.resize( strlen( str.data() ) + 1 );
나는 주로 이것을 사용합니다.
std::string myformat(const char *const fmt, ...)
{
char *buffer = NULL;
va_list ap;
va_start(ap, fmt);
(void)vasprintf(&buffer, fmt, ap);
va_end(ap);
std::string result = buffer;
free(buffer);
return result;
}
단점: 모든 시스템이 VASprint를 지원하지는 않습니다.
이 코드는 제 프로그램에서 사용하는 코드입니다.화려하지는 않지만, 효과가 있습니다.참고로 필요에 따라 크기를 조정해야 합니다. MAX_BUFFER는 1024입니다.
std::string Format ( const char *fmt, ... )
{
char textString[MAX_BUFFER*5] = {'\0'};
// -- Empty the buffer properly to ensure no leaks.
memset(textString, '\0', sizeof(textString));
va_list args;
va_start ( args, fmt );
vsnprintf ( textString, MAX_BUFFER*5, fmt, args );
va_end ( args );
std::string retStr = textString;
return retStr;
}
데이카브와 픽셀포인트의 대답에서 아이디어를 얻었습니다.좀 놀다가 이렇게 됐어요.
#include <cstdarg>
#include <cstdio>
#include <string>
std::string format(const char* fmt, ...)
{
va_list vl;
va_start(vl, fmt);
int size = vsnprintf(0, 0, fmt, vl) + sizeof('\0');
va_end(vl);
char buffer[size];
va_start(vl, fmt);
size = vsnprintf(buffer, size, fmt, vl);
va_end(vl);
return std::string(buffer, size);
}
정상적인 프로그래밍 연습을 통해 코드가 충분해야 한다고 생각하지만, 여전히 충분히 간단하고 C++11이 필요하지 않은 더 안전한 대안에 대해 열려 있습니다.
첫 두 번째 통화를 방지하는 다른이 있습니다.vsnprintf()
초기 버퍼가 이미 충분할 때.
std::string format(const char* fmt, ...)
{
va_list vl;
int size;
enum { INITIAL_BUFFER_SIZE = 512 };
{
char buffer[INITIAL_BUFFER_SIZE];
va_start(vl, fmt);
size = vsnprintf(buffer, INITIAL_BUFFER_SIZE, fmt, vl);
va_end(vl);
if (size < INITIAL_BUFFER_SIZE)
return std::string(buffer, size);
}
size += sizeof('\0');
char buffer[size];
va_start(vl, fmt);
size = vsnprintf(buffer, size, fmt, vl);
va_end(vl);
return std::string(buffer, size);
}
(이 버전은 Piti Ongmongkolkul의 답변과 비슷하지만 사용하지 않는다는 것이 밝혀졌습니다.new
그리고.delete[]
은 " " " " 을 할 때 합니다.std::string
.
여기서 사용하지 않는 것에 대한 아이디어.new
그리고.delete[]
이는 할당 및 할당 해제 함수를 호출할 필요가 없기 때문에 힙을 통한 스택의 사용을 암시하는 것이지만, 제대로 사용되지 않을 경우 일부(오래된 시스템이거나 취약한 시스템일 수 있음) 시스템에서 버퍼 오버플로가 발생할 위험이 있습니다.만약 이것이 문제라면, 저는 다음을 사용할 것을 강력히 제안합니다.new
그리고.delete[]
대신.여기서 유일한 관심사는 다음과 같은 할당입니다.vsnprintf()
는 이미 제한과 함께 호출되었으므로 두 번째 버퍼에 할당된 크기를 기준으로 제한을 지정하면 이러한 제한도 방지됩니다.)
@iFreilicht 답변의 약간 수정된 버전 아래, C++14로 업데이트됨 (사용법:make_unique
선언 대신 을하여 raw 선언에 대한 했습니다.std::string
(케니 커 기사에 근거한) 주장
#include <iostream>
#include <memory>
#include <string>
#include <cstdio>
template <typename T>
T process_arg(T value) noexcept
{
return value;
}
template <typename T>
T const * process_arg(std::basic_string<T> const & value) noexcept
{
return value.c_str();
}
template<typename ... Args>
std::string string_format(const std::string& format, Args const & ... args)
{
const auto fmt = format.c_str();
const size_t size = std::snprintf(nullptr, 0, fmt, process_arg(args) ...) + 1;
auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, fmt, process_arg(args) ...);
auto res = std::string(buf.get(), buf.get() + size - 1);
return res;
}
int main()
{
int i = 3;
float f = 5.f;
char* s0 = "hello";
std::string s1 = "world";
std::cout << string_format("i=%d, f=%f, s=%s %s", i, f, s0, s1) << "\n";
}
출력:
i = 3, f = 5.000000, s = hello world
원하는 경우 이 답변을 원래 답변과 자유롭게 병합할 수 있습니다.
업데이트 1: 추가됨fmt::format
저는 여기에 소개된 방법들에 대해 스스로 조사를 했고 여기에 언급된 것과는 정반대의 결과를 얻었습니다.
4가지 방법에 걸쳐 4가지 기능을 사용했습니다.
- 함수 + 변수 함수 + 변수 함수 +
vsnprintf
+std::unique_ptr
- 함수 + 변수 함수 + 변수 함수 +
vsnprintf
+std::string
- 함수 + 변수 템플릿 함수 + 변수 템플릿 함수 +
std::ostringstream
+std::tuple
+utility::for_each
fmt::format
에서 합니다.fmt
는 " " " 입니다.googletest
사용했습니다.
#include <string>
#include <cstdarg>
#include <cstdlib>
#include <memory>
#include <algorithm>
#include <fmt/format.h>
inline std::string string_format(size_t string_reserve, const std::string fmt_str, ...)
{
size_t str_len = (std::max)(fmt_str.size(), string_reserve);
// plain buffer is a bit faster here than std::string::reserve
std::unique_ptr<char[]> formatted;
va_list ap;
va_start(ap, fmt_str);
while (true) {
formatted.reset(new char[str_len]);
const int final_n = vsnprintf(&formatted[0], str_len, fmt_str.c_str(), ap);
if (final_n < 0 || final_n >= int(str_len))
str_len += (std::abs)(final_n - int(str_len) + 1);
else
break;
}
va_end(ap);
return std::string(formatted.get());
}
inline std::string string_format2(size_t string_reserve, const std::string fmt_str, ...)
{
size_t str_len = (std::max)(fmt_str.size(), string_reserve);
std::string str;
va_list ap;
va_start(ap, fmt_str);
while (true) {
str.resize(str_len);
const int final_n = vsnprintf(const_cast<char *>(str.data()), str_len, fmt_str.c_str(), ap);
if (final_n < 0 || final_n >= int(str_len))
str_len += (std::abs)(final_n - int(str_len) + 1);
else {
str.resize(final_n); // do not forget to shrink the size!
break;
}
}
va_end(ap);
return str;
}
template <typename... Args>
inline std::string string_format3(size_t string_reserve, Args... args)
{
std::ostringstream ss;
if (string_reserve) {
ss.rdbuf()->str().reserve(string_reserve);
}
std::tuple<Args...> t{ args... };
utility::for_each(t, [&ss](auto & v)
{
ss << v;
});
return ss.str();
}
그for_each
여기서 구현: 튜플을 통해 반복
#include <type_traits>
#include <tuple>
namespace utility {
template <std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, const FuncT &)
{
}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> & t, const FuncT & f)
{
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
}
테스트:
TEST(ExternalFuncs, test_string_format_on_unique_ptr_0)
{
for (size_t i = 0; i < 1000000; i++) {
const std::string v = string_format(0, "%s+%u\n", "test test test", 12345);
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_string_format_on_unique_ptr_256)
{
for (size_t i = 0; i < 1000000; i++) {
const std::string v = string_format(256, "%s+%u\n", "test test test", 12345);
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_string_format_on_std_string_0)
{
for (size_t i = 0; i < 1000000; i++) {
const std::string v = string_format2(0, "%s+%u\n", "test test test", 12345);
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_string_format_on_std_string_256)
{
for (size_t i = 0; i < 1000000; i++) {
const std::string v = string_format2(256, "%s+%u\n", "test test test", 12345);
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_string_format_on_string_stream_on_variadic_tuple_0)
{
for (size_t i = 0; i < 1000000; i++) {
const std::string v = string_format3(0, "test test test", "+", 12345, "\n");
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_string_format_on_string_stream_on_variadic_tuple_256)
{
for (size_t i = 0; i < 1000000; i++) {
const std::string v = string_format3(256, "test test test", "+", 12345, "\n");
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_string_format_on_string_stream_inline_0)
{
for (size_t i = 0; i < 1000000; i++) {
std::ostringstream ss;
ss << "test test test" << "+" << 12345 << "\n";
const std::string v = ss.str();
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_string_format_on_string_stream_inline_256)
{
for (size_t i = 0; i < 1000000; i++) {
std::ostringstream ss;
ss.rdbuf()->str().reserve(256);
ss << "test test test" << "+" << 12345 << "\n";
const std::string v = ss.str();
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_fmt_format_positional)
{
for (size_t i = 0; i < 1000000; i++) {
const std::string v = fmt::format("{0:s}+{1:d}\n", "test test test", 12345);
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
TEST(ExternalFuncs, test_fmt_format_named)
{
for (size_t i = 0; i < 1000000; i++) {
const std::string v = fmt::format("{first:s}+{second:d}\n", fmt::arg("first", "test test test"), fmt::arg("second", 12345));
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
}
}
그UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR
.
unseed.hpp:
#define UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(var) ::utility::unused_param(&var)
namespace utility {
extern const volatile void * volatile g_unused_param_storage_ptr;
extern void
#ifdef __GNUC__
__attribute__((optimize("O0")))
#endif
unused_param(const volatile void * p);
}
used.cpp:
namespace utility {
const volatile void * volatile g_unused_param_storage_ptr = nullptr;
void
#ifdef __GNUC__
__attribute__((optimize("O0")))
#endif
unused_param(const volatile void * p)
{
g_unused_param_storage_ptr = p;
}
}
결과:
[ RUN ] ExternalFuncs.test_string_format_on_unique_ptr_0
[ OK ] ExternalFuncs.test_string_format_on_unique_ptr_0 (556 ms)
[ RUN ] ExternalFuncs.test_string_format_on_unique_ptr_256
[ OK ] ExternalFuncs.test_string_format_on_unique_ptr_256 (331 ms)
[ RUN ] ExternalFuncs.test_string_format_on_std_string_0
[ OK ] ExternalFuncs.test_string_format_on_std_string_0 (457 ms)
[ RUN ] ExternalFuncs.test_string_format_on_std_string_256
[ OK ] ExternalFuncs.test_string_format_on_std_string_256 (279 ms)
[ RUN ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_0
[ OK ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_0 (1214 ms)
[ RUN ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_256
[ OK ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_256 (1325 ms)
[ RUN ] ExternalFuncs.test_string_format_on_string_stream_inline_0
[ OK ] ExternalFuncs.test_string_format_on_string_stream_inline_0 (1208 ms)
[ RUN ] ExternalFuncs.test_string_format_on_string_stream_inline_256
[ OK ] ExternalFuncs.test_string_format_on_string_stream_inline_256 (1302 ms)
[ RUN ] ExternalFuncs.test_fmt_format_positional
[ OK ] ExternalFuncs.test_fmt_format_positional (288 ms)
[ RUN ] ExternalFuncs.test_fmt_format_named
[ OK ] ExternalFuncs.test_fmt_format_named (392 ms)
를 통해 구현을 볼 수 .vsnprintf
+std::string
와같과 .fmt::format
하지만 그것을 통과하는 것보다 더 빠릅니다.vsnprintf
+std::unique_ptr
그것은 그것을 통과하는 것보다 더 빠릅니다.std::ostringstream
.
는 컴파일날에서 되었습니다.Visual Studio 2015 Update 3
다음 시간에 실행Windows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB
.
C++17 솔루션(std::string 및 std::wstring 모두에 적용됨):
버퍼를 할당하고 포맷한 후 다른 문자열에 복사하는 것은 효율적이지 않습니다.포맷된 문자열과 형식의 크기로 std:: 문자열을 해당 문자열 버퍼에 직접 생성할 수 있습니다.
#include <string>
#include <stdexcept>
#include <cwchar>
#include <cstdio>
#include <type_traits>
template<typename T, typename ... Args>
std::basic_string<T> string_format(T const* const format, Args ... args)
{
int size_signed{ 0 };
// 1) Determine size with error handling:
if constexpr (std::is_same_v<T, char>) { // C++17
size_signed = std::snprintf(nullptr, 0, format, args ...);
}
else {
size_signed = std::swprintf(nullptr, 0, format, args ...);
}
if (size_signed <= 0) {
throw std::runtime_error("error during formatting.");
}
const auto size = static_cast<size_t>(size_signed);
// 2) Prepare formatted string:
std::basic_string<T> formatted(size, T{});
if constexpr (std::is_same_v<T, char>) { // C++17
std::snprintf(formatted.data(), size + 1, format, args ...); // +1 for the '\0' (it will not be part of formatted).
}
else {
std::swprintf(formatted.data(), size + 1, format, args ...); // +1 for the '\0' (it will not be part of formatted).
}
return formatted; // Named Return Value Optimization (NRVO), avoids an unnecessary copy.
}
또한 format 매개 변수는 char[] / wchar_t[]이며 std::string 개체를 만드는 것은 효율적이지 않습니다.char* 또는 wchar_t*를 전달하고 std::string 개체가 이미 있는 경우에도_string.c_str()으로 사용할 수 있습니다.예:
int main()
{
int i{ 0 };
// The format parameter is a char[] / wchar_t[]:
const std::string title1 = string_format("story[%d].", ++i); // => "story[1]"
const std::wstring title2 = string_format(L"story[%d].", ++i); // => L"story[2]"
// If you already have a std::string object:
const std::string format1{ "story[%d]." };
const std::string title3 = string_format(format1.c_str(), ++i); // => "story[3]"
const std::wstring format2{ L"story[%d]." };
const std::wstring title4 = string_format(format2.c_str(), ++i); // => L"story[4]"
}
여기까지 나온 모든 답변은 다음과 같은 문제 중 하나 이상을 가지고 있는 것으로 보입니다. (1) VC++에서 작동하지 않을 수 있음 (2) 부스트 또는 fmt와 같은 추가 종속성이 필요함 (3) 사용자 지정 구현이 너무 복잡하고 테스트가 잘 되지 않을 수 있습니다.
아래 코드는 위의 모든 문제를 해결합니다.
#include <string>
#include <cstdarg>
#include <memory>
std::string stringf(const char* format, ...)
{
va_list args;
va_start(args, format);
#ifndef _MSC_VER
//GCC generates warning for valid use of snprintf to get
//size of result string. We suppress warning with below macro.
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
size_t size = std::snprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
std::unique_ptr<char[]> buf(new char[ size ] );
std::vsnprintf(buf.get(), size, format, args);
return std::string(buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
#else
int size = _vscprintf(format, args);
std::string result(size, 0);
vsnprintf_s((char*)result.data(), size + 1, _TRUNCATE, format, args);
return result;
#endif
va_end(args);
}
int main() {
float f = 3.f;
int i = 5;
std::string s = "hello!";
auto rs = stringf("i=%d, f=%f, s=%s", i, f, s.c_str());
printf("%s", rs.c_str());
return 0;
}
주의:
- ++를 더 사용하지 않기로 의 VC합니다.
snprintf
위의 높은 투표율을 가진 다른 답변에 대해 컴파일러 경고를 생성합니다.저는 항상 "오류로 경고" 모드로 실행하기 때문에, 그것은 저에게 적합하지 않습니다. - 는 함는다허니다용합을음을 받습니다.
char *
에std::string
왜냐하면 대부분의 경우 이 함수는 실제로 다음과 같은 리터럴 문자열로 호출될 것이기 때문입니다.char *
,것은 아니다.std::string
만약 당신이 가지고 있다면요.std::string
형식 매개 변수로, 그런 다음 그냥 호출합니다..c_str()
. - 함수의 이름은 printf, scanf 등을 유지하기 위해 string_format 대신 stringf입니다.
- 안전 문제(즉, 잘못된 매개 변수는 잠재적으로 예외 대신 세그먼트 결함을 발생시킬 수 있음)를 해결하지 못합니다.만약 당신이 이것이 필요하다면, 당신은 boost나 fmt 라이브러리를 사용하는 것이 더 낫습니다.여기서 제가 선호하는 것은 boost보다 이상한 형식 구문을 덜 가지면서 프로젝트에 넣는 것은 하나의 헤더와 소스 파일이기 때문입니다.그러나 둘 다 printf 형식 문자열과 호환되지 않으므로 아래와 같은 경우에도 유용합니다.
- stringf 코드는 GCC strict 모드 컴파일을 통과합니다.이것은 추가로 필요합니다.
#pragma
매크로를 사용하여 GCC 경고의 잘못된 긍정을 억제할 수 있습니다.
위의 코드가 테스트되었습니다.
Poco Foundation 라이브러리에는 매우 편리한 형식 기능이 있어 형식 문자열과 값 모두에서 std:: 문자열을 지원합니다.
- 문서: http://pocoproject.org/docs/Poco.html#7308
- 출처: https://github.com/pocoproject/poco/blob/develop/Foundation/src/Format.cpp
iomanip 헤더 파일을 사용하여 C++ 출력을 cout 형식으로 지정할 수 있습니다.set precision, set fill 등의 도우미 기능을 사용하기 전에 iomanip 헤더 파일을 포함해야 합니다.
여기 제가 "누적"한 벡터의 평균 대기 시간을 인쇄하기 위해 과거에 사용한 코드 조각이 있습니다.
#include<iomanip>
#include<iostream>
#include<vector>
#include<numeric>
...
cout<< "Average waiting times for tasks is " << setprecision(4) << accumulate(all(waitingTimes), 0)/double(waitingTimes.size()) ;
cout << " and " << Q.size() << " tasks remaining" << endl;
다음은 C++ 스트림을 포맷하는 방법에 대한 간단한 설명입니다.http://www.cprogramming.com/tutorial/iomanip.html
버퍼가 문자열을 인쇄할 수 있을 정도로 크지 않으면 문제가 발생할 수 있습니다.형식 지정된 메시지를 인쇄하기 전에 형식 지정된 문자열의 길이를 결정해야 합니다.나는 이것(Windows 및 Linux GCC에서 테스트됨)에 대한 도우미를 만들고, 당신은 그것을 사용해 볼 수 있습니다.
String.cpp: http://pastebin.com/DnfvzyKP
끈.h: http://pastebin.com/7U6iCUMa
String.cpp:
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <string>
using ::std::string;
#pragma warning(disable : 4996)
#ifndef va_copy
#ifdef _MSC_VER
#define va_copy(dst, src) dst=src
#elif !(__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
#define va_copy(dst, src) memcpy((void*)dst, (void*)src, sizeof(*src))
#endif
#endif
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw() {
int length;
va_list apStrLen;
va_copy(apStrLen, ap);
length = vsnprintf(NULL, 0, format, apStrLen);
va_end(apStrLen);
if (length > 0) {
dst.resize(length);
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
} else {
dst = "Format error! format: ";
dst.append(format);
}
}
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw() {
va_list ap;
va_start(ap, format);
toString(dst, format, ap);
va_end(ap);
}
///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw() {
string dst;
va_list ap;
va_start(ap, format);
toString(dst, format, ap);
va_end(ap);
return dst;
}
///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw() {
string dst;
toString(dst, format, ap);
return dst;
}
int main() {
int a = 32;
const char * str = "This works!";
string test(toString("\nSome testing: a = %d, %s\n", a, str));
printf(test.c_str());
a = 0x7fffffff;
test = toString("\nMore testing: a = %d, %s\n", a, "This works too..");
printf(test.c_str());
a = 0x80000000;
toString(test, "\nMore testing: a = %d, %s\n", a, "This way is cheaper");
printf(test.c_str());
return 0;
}
String.h:
#pragma once
#include <cstdarg>
#include <string>
using ::std::string;
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw();
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw();
///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw();
///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw();
이것은 시도할 수 있습니다. 간단합니다.문자열 클래스의 뉘앙스를 사용하지 않습니다.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <exception>
using namespace std;
//---------------------------------------------------------------------
class StringFormatter
{
public:
static string format(const char *format, ...);
};
string StringFormatter::format(const char *format, ...)
{
va_list argptr;
va_start(argptr, format);
char *ptr;
size_t size;
FILE *fp_mem = open_memstream(&ptr, &size);
assert(fp_mem);
vfprintf (fp_mem, format, argptr);
fclose (fp_mem);
va_end(argptr);
string ret = ptr;
free(ptr);
return ret;
}
//---------------------------------------------------------------------
int main(void)
{
string temp = StringFormatter::format("my age is %d", 100);
printf("%s\n", temp.c_str());
return 0;
}
언급URL : https://stackoverflow.com/questions/2342162/stdstring-formatting-like-sprintf
'programing' 카테고리의 다른 글
리치 텍스트 상자에 이미지를 삽입하려면 어떻게 해야 합니까? (0) | 2023.05.21 |
---|---|
Swift를 사용하여 텍스트를 클립보드/페이스트보드에 복사하는 방법 (0) | 2023.05.21 |
VS 2008 중단점은 현재 적중되지 않습니다.이 문서에 대해 로드된 기호가 없습니다. (0) | 2023.05.21 |
Node.js의 HTML 파서 (0) | 2023.05.21 |
VB.NET 'If' 문에 'Or' 조건부가 모두 평가되었습니까? (0) | 2023.05.21 |