2009-02-05 5 views
12

저는 C++로 일하고 있습니다.sprintf가 작성하는 문자 수를 결정할 방법이 있습니까?

sprintf를 사용하여 잠재적으로 매우 긴 형식의 문자열을 쓰고 싶습니다 (특히 _snprintf_s와 같은 안전한 계산 버전이지만 아이디어는 같습니다). 대략적인 길이는 컴파일 타임에 알려지지 않으므로 큰 정적 버퍼에 의존하기보다는 동적으로 할당 된 메모리를 사용해야 할 것입니다. 특정 sprintf 호출에 필요한 문자 수를 결정할 수있는 방법이 있습니까? 그래서 충분히 큰 버퍼를 가질 수 있습니까?

내 폴백은 포맷 문자열의 길이를 두 배로 늘린 다음 시도해 보는 것입니다. 제대로 작동한다면, 버퍼의 크기를 두 배로 늘린 다음 다시 시도해보십시오. 그것이 맞을 때까지 반복하십시오. 명확한 솔루션이 아닙니다.

길이를 얻기 위해 C99에서 snprintf에 NULL을 전달하는 것처럼 보입니다. 나는 그 기능을 감싸기위한 모듈을 만들 수 있다고 생각하지만, 그 아이디어에 미치지는 않습니다.

"/ dev/null"/ "nul"에 대한 fprintf가 대신 작동 할 수 있습니까? 다른 아이디어?

편집 : 다른 방법으로 "중간 크기 쓰기"를 선택하도록 sprintf를 "청크"할 수 있습니까? 가능한 경우 버퍼를 채우고 처리 한 다음 중단 한 부분부터 다시 채우기 시작합니다.

답변

0

내가 말하는 것과 동일한 기능을 찾았지만, C++에서 C99에 추가 된 기능이 현재 통합되어 있지 않으므로 C99 메서드와 같은 간단한 기능을 C++에서 사용할 수 없습니다. snprintf와 같은).

아마도 가장 좋은 방법은 stringstream 개체를 사용하는 것입니다. 그것은 분명히 쓰여진 sprintf 전화보다 약간 성가 시지만 작동 할 것입니다.

+0

왜 이것이 downvoted인지 확실하지 않습니다 ... –

+0

나는 그것을하지는 못했지만 어쩌면 그것이 끝난 이유를 볼 수 있습니다. snprintf()의 PD 버전이 있으므로 C99가 필요하지 않습니다. 아니면 질문에 특별히 stringstream보다는 printf() 솔루션을 요구했기 때문일 수도 있습니다. 나는 몰라, 오래 전 드라이브 다운 downvoters을 이해하려고 애를 썼다. – paxdiablo

2

2 단계 접근 방식을 사용합니다. 일반적으로 출력 문자열의 많은 부분이 특정 임계 값 아래에있을 것이며 소수 만 더 커질 것입니다.

1 단계, 4K와 같은 적당한 크기의 정적 버퍼를 사용하십시오. snprintf()은 기록되는 문자 수를 제한 할 수 있기 때문에 버퍼 오버 플로우가 발생하지 않습니다. 당신이 무엇을 snprintf()에서 반환 얻을 것입니다 것입니다 당신의 버퍼가 충분히 큰 경우 작성했습니다.

snprintf()에 대한 전화가 4K 미만인 경우, 버퍼를 사용하고 종료하십시오. 앞서 언급했듯이, 대부분의 호출은이를 수행해야합니다.

무대 2에 들어갈 때가 있습니다. snprintf()에 대한 호출이 4K 버퍼에 맞지 않으면 적어도 얼마나 큰 버퍼가 필요한지 알 수 있습니다.

malloc()과 함께 할당 할 수있는 버퍼를 할당 한 다음 다시 새 버퍼에 snprintf()을 할당합니다. 버퍼를 다 끝냈 으면 해방하십시오.

우리는 snprintf() 이전의 시스템에서 작업했으며 파일 핸들을 /dev/null에 연결하고 fprintf()을 사용하여 동일한 결과를 얻었습니다./dev/null은 여러분이 제공하는만큼 많은 데이터를 취할 수 있도록 보장되었으므로 실제로 크기를 얻은 다음 필요한 경우 버퍼를 할당합니다.

종류의 그 모든 시스템 snprintf()이 (예를 들어, 나는 그것이 마이크로 소프트 C에서 _snprintf()을의 이해) 그래서 당신은 동일한 작업을 수행하는 기능을 찾거나 fprintf /dev/null 솔루션에 복귀 할 수 있습니다에 보관하십시오.

데이터가 크기 확인 snprintf()과 버퍼에 실제로 적용된 snprintf() (즉, 스레드의 경우) 사이에서 변경할 수있는 경우에도주의해야합니다. 크기가 커지면 버퍼 오버플로 손상이 발생합니다.

일단 함수로 전달 된 데이터가 반환 될 때까지 독점적으로 해당 함수에 속한다는 규칙을 따르는 경우 문제가되지 않습니다.

+0

아쉽게도 snprintf() 함수는 표준 C++가 아닙니다. 방금 Visual Studio 2008 Express Edition과 함께 사용하려고했는데 컴파일러가 snprintf를 찾을 수 없다고보고합니다. – jasonmray

+0

나는 그것이 마이크로 소프트 C++ – FryGuy

+0

@rubancache에서 _snprintf()라고 생각한다. "fprintf to/dev/null"솔루션을 사용할 때이다. – paxdiablo

0

CodeProject: CString-clone Using Standard C++을 살펴보십시오. 버퍼 크기를 늘리면 제안한 솔루션을 사용합니다.

 
// ------------------------------------------------------------------------- 
    // FUNCTION: FormatV 
    //  void FormatV(PCSTR szFormat, va_list, argList); 
    //
// DESCRIPTION: // This function formats the string with sprintf style format-specs. // It makes a general guess at required buffer size and then tries // successively larger buffers until it finds one big enough or a // threshold (MAX_FMT_TRIES) is exceeded. // // PARAMETERS: // szFormat - a PCSTR holding the format of the output // argList - a Microsoft specific va_list for variable argument lists // // RETURN VALUE: // -------------------------------------------------------------------------

void FormatV(const CT* szFormat, va_list argList) 
{ 
#ifdef SS_ANSI 

    int nLen = sslen(szFormat) + STD_BUF_SIZE; 
    ssvsprintf(GetBuffer(nLen), nLen-1, szFormat, argList); 
    ReleaseBuffer(); 
#else 
    CT* pBuf   = NULL; 
    int nChars   = 1; 
    int nUsed   = 0; 
    size_type nActual = 0; 
    int nTry   = 0; 

    do 
    { 
     // Grow more than linearly (e.g. 512, 1536, 3072, etc) 

     nChars   += ((nTry+1) * FMT_BLOCK_SIZE); 
     pBuf   = reinterpret_cast<CT*>(_alloca(sizeof(CT)*nChars)); 
     nUsed   = ssnprintf(pBuf, nChars-1, szFormat, argList); 

     // Ensure proper NULL termination. 
     nActual   = nUsed == -1 ? nChars-1 : SSMIN(nUsed, nChars-1); 
     pBuf[nActual+1]= '\0'; 


    } while (nUsed < 0 && nTry++ < MAX_FMT_TRIES); 

    // assign whatever we managed to format 

    this->assign(pBuf, nActual); 
#endif 
} 

21

The man page for snprintf는 말한다 :

 
    Return value 
     Upon successful return, these functions return the number of 
     characters printed (not including the trailing '\0' used to end 
     output to strings). The functions snprintf and vsnprintf do not 
     write more than size bytes (including the trailing '\0'). If 
     the output was truncated due to this limit then the return value 
     is the number of characters (not including the trailing '\0') 
     which would have been written to the final string if enough 
     space had been available. Thus, a return value of size or more 
     means that the output was truncated. (See also below under 
     NOTES.) If an output error is encountered, a negative value is 
     returned. 

이것이 의미하는 것은 당신이 작성 얻을 것이다 0 아무것도의 크기 snprintf를 호출 할 수 있으며, 반환 값은 당신이 할당 할 필요가 얼마나 많은 공간을 당신에게 말할 것입니다 당신의 문자열 :

다른 언급했듯이
int how_much_space = snprintf(NULL, 0, fmt_string, param0, param1, ...); 
+0

대다수의 printf가 특정 크기보다 작기 때문에 먼저 고정 크기 스택 변수로 출력하는 것이 좋습니다. 즉, 대다수의 사용자는 print/malloc/print/free가 필요하지 않고 인쇄 만합니다. 한도를 넘는 작은 숫자 만 완전한 행동이 필요합니다. – paxdiablo

+0

@Pax : 아마도 사실이지만 성능 최적화입니다. 종종 적절하지만 항상 그렇지는 않습니다. 그렇지 않은 경우의 예 : 많은 스택 공간이 없으며 많은 수의 인쇄물을 스택에 넣을 수있는 버퍼 크기보다 커야합니다. 그래서 항상 힙을 사용합니다. –

+0

이 시나리오에서 힙의 인쇄물에 _dedicated_ 부분의 메모리를 미리 할당하고 성능 최적화의 이점을 얻으십시오. – user666412

5

, snprintf()는 잘리지의 출력을 방지하기 위해 버퍼에 필요한 문자의 수를 반환합니다. 0 버퍼 길이 매개 변수로 간단하게 호출하여 필요한 크기를 얻은 다음 적절한 크기의 버퍼를 사용할 수 있습니다.

효율성을 약간 향상 시키려면 정상적인 경우에는 충분히 큰 버퍼로 호출하고 출력이 잘리는 경우 snprintf()으로 두 번째 전화를 겁니다. 이 경우 버퍼가 제대로 해제되도록하려면 보통 동적 메모리를 처리하는 auto_buffer<> 개체를 사용합니다 (정상적인 경우에는 힙 할당을 피하기 위해 스택에 기본 버퍼가 있습니다)).

Microsoft 컴파일러를 사용하는 경우 MS에는 버퍼가 종료되어야하는 것과 버퍼가 얼마나 커야하는지에 대한 심각한 제한이있는 비표준 _snprintf()이 있습니다.

Microsoft의 지원이 중단되지 않도록하려면 a nearly public domain snprintf() from Holger Weiss을 사용하십시오.

물론 MS가 아닌 C 또는 C++ 컴파일러에 snprintf()이 누락되어 있으면 위 링크의 코드도 올바르게 작동해야합니다.

0

C++을 사용하고 있으므로 실제로 sprintf 버전을 사용할 필요가 없습니다. 가장 간단한 방법은 std :: ostringstream을 사용하는 것입니다.

std::ostringstream oss; 
oss << a << " " << b << std::endl; 

oss.str()은 oss에 작성한 내용으로 std :: string을 반환합니다. const char *을 얻으려면 oss.str().c_str()을 사용하십시오. 장기적으로 처리하기가 훨씬 쉬워지고 메모리 누수 또는 버퍼 오버런이 제거됩니다. 일반적으로 C++의 메모리 문제에 대해 걱정하고 있다면 언어를 최대한 활용하지 않고 디자인을 재고해야합니다.

+0

경고 단어 : C++은 스트리밍 기능에 많은 추가 기능을 포함하고 있으며, 이는 주요한 방법으로 당신을 물릴 수 있습니다. 특히 스트림은 로케일을 지원하므로 숫자가 형식화되는 방식을 변경할 수 있습니다. 한 로캘로 설정된 스트림에서 출력 된 숫자는 다른 로캘을 사용하여 스트림으로 읽을 수 없습니다. 다른 로케일이 사용되지 않는다고 보장 할 수 있다면 괜찮습니다. 우리는 로케일을 사용하는 호스트 응용 프로그램에 연결된 DLL을 사용했기 때문에이 문제에 물 렸습니다. – AHelps

+0

http://unixhelp.ed.ac.uk/CGI/man-cgi?sprintf+3 로케일에 관한 그게 뭐야? –

1

asprintf은이 기능을 관리하는 GNU 확장 프로그램입니다.형식 문자열과 변수 개수의 인수와 함께 포인터를 출력 인수로 받아들이고 결과를 포함하는 적절히 할당 된 버퍼의 주소를 다시 포인터에 씁니다.

당신은 너무처럼 사용할 수 있습니다

#define _GNU_SOURCE 
#include <stdio.h> 

int main(int argc, char const *argv[]) 
{ 
    char *hi = "hello"; // these could be really long 
    char *everyone = "world"; 
    char *message; 
    asprintf(&message, "%s %s", hi, everyone); 
    puts(message); 
    free(message); 
    return 0; 
} 

희망이 누군가를하는 데 도움이!

관련 문제