sprintf와 같은 std :: string 형식
형식 std::string
을 sprintf
지정하여 파일 스트림으로 보내야합니다. 어떻게해야합니까?
기본 버퍼에 대한 쓰기 액세스 권한이 없으므로 직접 수행 할 수 없습니다 (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();
vsnprintf()
내부적으로 사용하는 C ++ 11 솔루션 :
#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
요구 사항을 준수하기 위해 값으로 전달됩니다 va_start
.
참고 : "safer"및 "faster"버전은 일부 시스템에서 작동하지 않습니다. 따라서 둘 다 여전히 나열됩니다. 또한 "빠른"은 사전 할당 단계의 정확성에 전적으로 달려 있으며, 그렇지 않으면 strcpy
렌더링 속도가 느려집니다.
이용 C ++ (11) std::snprintf
이 꽤 쉽고 안전하게 작업이된다. 고정 된 버퍼 길이와 변수를 사용하는 C ++ 11 이전에 작성된이 질문에 대한 많은 답변을 보았습니다. 안전, 효율성 및 선명도 이유로 권장하지 않습니다.
#include <memory>
#include <iostream>
#include <string>
#include <cstdio>
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'
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
}
위의 코드 스 니펫은 CC0 1.0에 따라 라이센스가 부여되었습니다 .
라인 별 설명 :
목표 : A와 쓰기char*
사용하여std::snprintf
A와 그 변환 다음과std::string
.
먼저, 원하는 char 배열 길이를 결정합니다.
에서 cppreference.com :
반환 값
[...] buf_size 제한으로 인해 결과 문자열이 잘리는 경우 함수는 제한이 적용되지 않은 경우 쓰여질 총 문자 수 (종료 null 바이트 제외)를 반환합니다.
이것은 원하는 크기가 문자 수 에 1을 더한 것을 의미 하므로 널 종료자는 다른 모든 문자 뒤에 앉아서 문자열 생성자에 의해 다시 잘릴 수 있습니다. 이 문제는 의견에서 @ alexk7에 의해 설명되었습니다.
그런 다음 새 문자 배열을 할당하고에 할당합니다 std::unique_ptr
. 수동으로 delete
다시 할 필요가 없으므로 일반적으로 권장 됩니다.
unique_ptr
생성자가 예외를 발생시키는 경우 메모리 할당을 해제 할 수 없으므로 사용자 정의 형식 으로을 할당하는 안전한 방법은 아닙니다 !
그 후, 우리는 물론 snprintf
의도 된 용도로 사용하고 형식화 된 문자열을 쓰고 char[]
나중에 새 std::string
것으로 작성하여 반환 할 수 있습니다.
std::string
인수 목록 에서도 사용하려면 이 요점을 살펴보십시오 .
Visual Studio 사용자를 위한 추가 정보 :
이 답변 에서 설명한대로 Microsoft는 이름 std::snprintf
을 _snprintf
(예,없이 std::
) 로 바꿨 습니다 . MS는 더 이상 사용하지 않는 것으로 설정하고 _snprintf_s
대신 사용하는 것이 좋습니다. 그러나 _snprintf_s
버퍼가 포맷 된 출력보다 0보다 작거나 작을 수는 없으며 출력 길이가 발생하면 계산 길이를 계산하지 않습니다. 따라서 컴파일 중에 사용 중단 경고를 제거하기 위해 파일 맨 위에 다음을 사용하는 다음 행 을 삽입 할 수 있습니다 _snprintf
.
#pragma warning(disable : 4996)
boost::format()
원하는 기능을 제공합니다.
Boost 형식 라이브러리 개요에서와 같이 :
format 객체는 format-string으로 구성되며 operator %에 대한 반복 호출을 통해 인수가 제공됩니다. 그런 다음 각 인수는 문자열로 변환되어 format-string에 따라 하나의 문자열로 결합됩니다.
#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"
C ++ 20에는 API std::format
와 유사 sprintf
하지만 완전히 유형 안전하고 사용자 정의 유형과 작동하며 Python과 같은 형식 문자열 구문을 사용하는 것이 포함됩니다.
#include <format>
std::string result = std::format("The answer is {}.", 42);
여기의 대부분의 다른 답변에 관해서는 불행히도 varargs를 사용 format
하며 리터럴 형식 문자열에서만 작동하는 GCC 속성 과 같은 것을 사용하지 않으면 본질적으로 안전하지 않습니다 . 다음 예제에서 이러한 기능이 안전하지 않은 이유를 확인할 수 있습니다.
std::string format_str = "%s";
string_format(format_str, format_str[0]);
string_format
Erik Aronesty의 답변에서 구현 한 부분 은 어디 입니까? 이 코드는 컴파일되지만 실행하려고하면 충돌이 발생합니다.
$ g++ -Wall -Wextra -pedantic test.cc
$ ./a.out
Segmentation fault: 11
(가변) 템플릿을 사용하여 금고를 구현하고 printf
형식으로 확장 할 수 std::string
있습니다. 이것은에서 수행 된 {FMT} 라이브러리 및 std::format
이는에 안전한 대안 제공 sprintf
반환 std::string
(또는 제공된 버퍼에 쓰는) :
#include <fmt/core.h>
std::string format_str = "The answer is {}.";
std::string result = fmt::format(format_str, 42);
{fmt}는 인수 유형을 추적하며 유형이 형식 스펙과 일치하지 않으면 분할 결함이없고 constexpr
형식 문자열 검사가 사용되는 경우 예외 또는 컴파일 타임 오류 만 있습니다.
면책 조항 : 저는 {fmt} 및 C ++ 20의 저자입니다 std::format
.
printf와 같은 구문 만 원한다면 (printf를 직접 호출하지 않고) Boost Format을 살펴보십시오 .
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
'sprintf'방식으로 포맷하려면 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;
}
예를 들어 GCC에서 C ++ 11 지원으로 컴파일하십시오. g++ -std=c++11
용법:
std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);
[편집 '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;
}
이것은 실제로 방해가되는 <<
연산자 의 쉼표로 구분 된 버전 (대신)이며 다음 과 같이 사용됩니다.
char chSpace=' ';
double pi=3.1415;
std::string sWorld="World", str_var;
str_var = vtspf("Hello", ',', chSpace, sWorld, ", pi=", pi);
[편집] 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'-way를 좋아하는 사람들에게는 다음과 같은 기능을 사용하고 있습니다. 원하는 경우 스프린트 항목에 더 가깝게 % 옵션을 확장 할 수 있습니다. 거기에있는 것들은 현재 나의 필요에 충분합니다. sprintf와 동일하게 stringf () 및 stringfappend ()를 사용합니다. ...의 매개 변수는 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;
}
구글이하는 방법은 다음과 같습니다 : StringPrintf
(BSD 라이센스)
, 페이스 북은 비슷한 방식으로 수행합니다 : StringPrintf
(아파치 라이센스)
둘 다 편리한 기능을 제공합니다 StringAppendF
.
이 매우 인기있는 질문에 대한 나의 두 센트.
-like 함수 의 맨 페이지printf
를 인용하려면 :
성공적으로 리턴되면이 함수는 인쇄 된 문자 수를 리턴합니다 (널로 출력을 종료하는 데 사용되는 널 바이트 제외).
snprintf () 및 vsnprintf () 함수는 크기가 바이트를 초과하지 않습니다 (종료 널 바이트 ( '\ 0') 포함). 이 한계로 인해 출력이 잘린 경우 리턴 값은 충분한 공간이 사용 가능한 경우 최종 문자열에 기록 된 문자 수 (종료 널 바이트 제외)입니다. 따라서 크기 이상의 반환 값은 출력이 잘린 것을 의미합니다.
다시 말해, 제정신 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);
}
그것은 꽤 잘 작동합니다 :)
Variadic 템플릿은 C ++ 11에서만 지원됩니다. pixelpoint의 답변은 이전 프로그래밍 스타일을 사용하는 유사한 기술을 보여줍니다.
C ++에 즉시 그러한 것들이없는 것은 이상합니다. 그들은 최근에을 추가 to_string()
했습니다. 제 생각에는 큰 발전입니다. 그들이 결국에 .format
연산자를 추가할지 궁금합니다 std::string
...
편집하다
alexk7이 지적했듯이 바이트의 공간이 필요하므로 +1
의 반환 값에 A 가 필요합니다 . 직관적으로, 대부분의 아키텍쳐에서는를 생략 하여 정수를 부분적으로 덮어 씁니다 . 에 대한 실제 매개 변수로 평가 한 후에 발생 하므로 효과가 표시되지 않아야합니다.std::snprintf
\0
+1
required
0
required
std::snprintf
그러나이 문제는 컴파일러 최적화와 같이 변경 될 수 있습니다. 컴파일러가 required
변수에 레지스터를 사용하기로 결정하면 어떻게됩니까? 이것은 때때로 보안 문제를 야기하는 일종의 오류입니다.
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 사용
테스트를 거친 생산 품질 답변
이 답변은 표준 준수 기술로 일반적인 경우를 처리합니다. 페이지 하단 근처의 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; }
예측 가능한 선형 효율
질문 사양에 따라 안전하고 신뢰할 수 있으며 예측 가능한 재사용 가능한 기능을 위해서는 두 가지 패스가 필요합니다. 재사용 가능한 함수에서 vargs 크기 분포에 대한 가정은 잘못된 프로그래밍 스타일이므로 피해야합니다. 이 경우, 임의의 큰 변수 길이 표현이 알고리즘 선택의 핵심 요소입니다.
오버 플로우시 재 시도는 기하 급수적으로 비효율적이며, 이는 쓰기 버퍼가 널 (null) 일 때 드라 이런을 제공하기 위해 C ++ 11 표준위원회가 상기 제안을 논의했을 때 논의 된 또 다른 이유입니다.
위의 프로덕션 준비 구현에서 첫 번째 실행은 할당 크기를 결정하는 건식 실행입니다. 할당이 발생하지 않습니다. printf 지시문의 구문 분석과 vargs의 읽기는 수십 년 동안 매우 효율적으로 이루어졌습니다. 사소한 경우에 대한 작은 비 효율성이 희생되어야하더라도 재사용 가능한 코드는 예측 가능해야합니다.
보안 및 신뢰성
앤드류 코니 그 (Andrew Koenig)는 케임브리지 행사에서 강연을 마친 후 소규모 그룹에 "사용자 기능이 예외적 인 기능 실패에 대한 착취에 의존해서는 안된다"고 말했다. 평소와 같이, 그의 지혜는 그 이후로 기록에서 사실로 나타났습니다. 수정 및 닫힌 보안 버그 문제는 종종 수정 전에 악용 된 취약점에 대한 설명에서 재시도 해킹을 나타냅니다.
이는 Sprintf 대안, C9X 개정 제안 , ISO IEC 문서 WG14 N645 / X3J11 96-008 의 널 버퍼 기능에 대한 공식 표준 개정 제안에 언급되어 있습니다. 동적 메모리 가용성의 제약 조건 내에서 인쇄 지시문 당 "% s"(으)로 삽입 된 임의로 긴 문자열은 예외가 아니며 "예외 기능"을 생성하기 위해 활용해서는 안됩니다.
이 답변의 첫 번째 단락에 링크 된 C ++ Reference.org 페이지의 맨 아래에 제공된 예제 코드와 함께 제안을 고려하십시오.
또한 실패 사례 테스트는 성공 사례만큼 강력하지 않습니다.
이식성
모든 주요 OS 공급 업체는 c ++ 11 표준의 일부로 std :: vsnprintf를 완벽하게 지원하는 컴파일러를 제공합니다. 더 이상 배포를 유지하지 않는 공급 업체 제품을 실행하는 호스트에는 여러 가지 이유로 g ++ 또는 clang ++이 제공되어야합니다.
스택 사용
std :: vsnprintf에 대한 첫 번째 호출에서 스택 사용은 두 번째 호출보다 작거나 같으며 두 번째 호출이 시작되기 전에 해제됩니다. 첫 번째 호출이 스택 가용성을 초과하면 std :: fprintf도 실패합니다.
C ++ 20 std::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);
}
지원이 GCC에 도착하면 GCC 9.1.0이 g++-9 -std=c++2a
아직 지원하지 않을 때 시도해 보겠습니다 .
API는 새로운 std::format
헤더 를 추가합니다 :
제안 된 형식 지정 API는 새 헤더에 정의되어 있으며
<format>
기존 코드에는 영향을 미치지 않습니다.
기존 fmt
라이브러리는 polyfill이 필요한 경우이를 구현한다고 주장합니다. https://github.com/fmtlib/fmt
C ++ 20 구현
std::format
.
sprintf와 같은 std :: string 형식 에서 이전에 언급되었습니다.
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
double 또는 float를 인쇄 할 때 소수점 이하 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 );
asprintf (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;
}
이것은 내 프로그램 에서이 작업을 수행하는 데 사용하는 코드입니다 ... 멋진 것은 아니지만 트릭을 수행합니다 ... 참고, 적용 가능한 크기를 조정해야합니다. 나를위한 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;
}
Dacav 와 pixelpoint의 답변 에서 아이디어를 얻었습니다 . 나는 조금 놀았고 이것을 얻었다 :
#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[]
, and 만 사용하지 않으며 생성 할 때 크기도 지정합니다 std::string
.
여기에 사용하지 않는 생각 new
과 delete[]
는 통화 할당 및 할당 해제 기능을 필요로하지 않기 때문에 제대로 사용하지 그러나 경우, 힙을 통해 스택의 사용을 의미하는 것입니다, 일부 (아마도 이전에 버퍼 오버 플로우 위험, 또는 수 아마도 취약한 시스템 일 것입니다. 이것이 우려되는 경우 new
및 delete[]
대신 사용하는 것이 좋습니다 . 여기서 vsnprintf()
제한은 이미 제한으로 호출 된 할당에 관한 것이므로 두 번째 버퍼에 할당 된 크기를 기준으로 제한을 지정하면이를 방지 할 수 있습니다.)
나는 보통 이것을 사용합니다 :
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를 지원하는 것은 아닙니다
@iFreilicht 답변의 약간 수정 된 버전 아래에서 C ++ 14 ( make_unique
원시 선언 대신 함수 사용)로 업데이트되고 std::string
인수 지원이 추가되었습니다 (Kenny Kerr article 기반 )
#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
원하는 경우이 답변을 원래 답변과 병합하십시오.
Poco Foundation 라이브러리에는 매우 편리한 형식 함수가 있으며 형식 문자열과 값 모두에서 std :: string을 지원합니다.
- 문서 : http://pocoproject.org/docs/Poco.html#7308
- 출처 : https://github.com/pocoproject/poco/blob/develop/Foundation/src/Format.cpp
iomanip 헤더 파일을 사용하여 cout에서 C ++ 출력을 형식화 할 수 있습니다. setprecision, setfill 등과 같은 도우미 함수를 사용하기 전에 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
_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str();
매우 간단한 솔루션.
std::string strBuf;
strBuf.resize(256);
int iCharsPrinted = sprintf_s((char *)strPath.c_str(), strPath.size(), ...);
strBuf.resize(iCharsPrinted);
업데이트 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
.
unsued.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);
}
미사용 .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
.
몇 가지 대답의 업데이트, 차이점은-함수가 % s에 대해 std :: string을 올바르게 수락 함
namespace format_helper
{
template <class Src>
inline Src cast(Src v)
{
return v;
}
inline const char *cast(const std::string& v)
{
return v.c_str();
}
};
template <typename... Ts>
inline std::string stringfmt (const std::string &fmt, Ts&&... vs)
{
using namespace format_helper;
char b;
size_t required = std::snprintf(&b, 0, fmt.c_str(), cast(std::forward<Ts>(vs))...);//not counting the terminating null character.
std::string result;
//because we use string as container, it adds extra 0 automatically
result.resize(required , 0);
//and snprintf will use n-1 bytes supplied
std::snprintf(const_cast<char*>(result.data()), required + 1, fmt.c_str(), cast(std::forward<Ts>(vs))...);
return result;
}
라이브 : http://cpp.sh/5ajsv
내가 선호하는 한 가지 해결책은 sprintf를 사용하여 std :: string 버퍼에 직접 sprintf를 사용하여 버퍼를 충분히 크게 만든 후이 작업을 수행하는 것입니다.
#include <string>
#include <iostream>
using namespace std;
string l_output;
l_output.resize(100);
for (int i = 0; i < 1000; ++i)
{
memset (&l_output[0], 0, 100);
sprintf (&l_output[0], "\r%i\0", i);
cout << l_output;
cout.flush();
}
따라서 std :: string을 만들고 크기를 조정하고 버퍼에 직접 액세스하십시오 ...
참고 URL : https://stackoverflow.com/questions/2342162/stdstring-formatting-like-sprintf
'development' 카테고리의 다른 글
자바 스크립트에서 문자열의 첫 문자 삭제 (0) | 2020.02.21 |
---|---|
플라스크 뷰에서 JSON 응답 반환 (0) | 2020.02.21 |
문자열에서 HTML 태그 제거 (0) | 2020.02.21 |
GIT 오류를 수정하는 방법 : 개체 파일이 비어 있습니까? (0) | 2020.02.21 |
현재 표시된 조각을 어떻게 얻습니까? (0) | 2020.02.21 |