[c] C 형식의 문자열 만들기 (인쇄하지 않음)

문자열을 받아들이는 함수가 있습니다.

void log_out(char *);

그것을 호출 할 때 다음과 같이 형식이 지정된 문자열을 즉석에서 만들어야합니다.

int i = 1;
log_out("some text %d", i);

ANSI C에서 어떻게해야합니까?


오직, sprintf()int를 반환하기 때문에 이것은 적어도 3 개의 명령을 작성해야 함을 의미합니다.

char *s;
sprintf(s, "%d\t%d", ix, iy);
log_out(s);

단축 할 방법이 있습니까?



답변

sprintf를 사용하십시오 .

int sprintf ( char * str, const char * format, ... );

형식화 된 데이터를 문자열에 쓰기 printf에서 형식이 사용 된 경우 인쇄되는 것과 동일한 텍스트로 문자열을 작성하지만 인쇄되는 대신 내용은 str이 가리키는 버퍼에 C 문자열로 저장됩니다.

버퍼의 크기는 전체 결과 문자열을 포함 할 수있을만큼 충분히 커야합니다 (안전한 버전은 snprintf 참조).

종료 널 문자는 컨텐츠 뒤에 자동으로 추가됩니다.

형식 매개 변수 뒤에 함수는 형식에 필요한만큼의 추가 인수를 예상합니다.

매개 변수 :

str

결과 C- 문자열이 저장되는 버퍼에 대한 포인터. 버퍼는 결과 문자열을 포함 할 수있을만큼 커야합니다.

format

printf의 형식과 동일한 사양을 따르는 형식 문자열을 포함하는 C 문자열 (자세한 내용은 printf 참조).

... (additional arguments)

형식 문자열에 따라 함수는 형식 문자열에서 형식 지정자를 대체하는 데 사용할 값을 포함하는 추가 인수 시퀀스 (또는 n의 경우 저장 위치에 대한 포인터)를 예상 할 수 있습니다. 최소한 형식 지정자에 지정된 값의 수만큼 이러한 인수가 있어야합니다. 추가 인수는 함수에서 무시됩니다.

예:

// Allocates storage
char *hello_world = (char*)malloc(13 * sizeof(char));
// Prints "Hello world!" on hello_world
sprintf(hello_world, "%s %s!", "Hello", "world");


답변

POSIX-2008 호환 시스템 (최신 Linux)을 사용하는 경우 안전하고 편리한 asprintf()기능을 사용할 수 있습니다. malloc()메모리 가 충분하므로 최대 문자열 크기에 대해 걱정할 필요가 없습니다. 다음과 같이 사용하십시오.

char* string;
if(0 > asprintf(&string, "Formatting a number: %d\n", 42)) return error;
log_out(string);
free(string);

이것은 안전한 방식으로 문자열을 구성하기 위해 얻을 수있는 최소한의 노력입니다. sprintf()당신이 질문에 준 코드는 깊이 결함이 :

  • 포인터 뒤에 할당 된 메모리가 없습니다. 메모리의 임의의 위치에 문자열을 쓰고 있습니다!

  • 썼더라도

    char s[42];

    괄호 안에 어떤 숫자를 넣어야할지 모르기 때문에 심각한 문제에 봉착 할 것입니다.

  • “안전한”변형을 사용 했더라도 snprintf()문자열이 잘릴 위험이 있습니다. 로그 파일에 쓸 때 이는 비교적 사소한 문제이지만 유용했을 정보를 정확하게 잘라낼 수있는 가능성이 있습니다. 또한 후행 끝줄 문자를 잘라내어 실패한 줄 끝에 다음 로그 줄을 붙입니다.

  • 당신의 조합을 사용하려고하면 malloc()하고 snprintf()모든 경우에 올바른 동작을 생산하기 위해, 당신은 약 두 배 많은 코드로 내가 주어진 것보다 함께 결국 asprintf(), 기본적으로의 기능을 재 프로그램 asprintf().


스타일 매개 변수 목록 자체를 log_out()취할 수 있는 래퍼를 제공하려는 경우를 인수로 취하는 printf()변형 vasprintf()을 사용할 수 있습니다 va_list. 다음은 이러한 래퍼의 완벽하게 안전한 구현입니다.

//Tell gcc that we are defining a printf-style function so that it can do type checking.
//Obviously, this should go into a header.
void log_out_wrapper(const char *format, ...) __attribute__ ((format (printf, 1, 2)));

void log_out_wrapper(const char *format, ...) {
    char* string;
    va_list args;

    va_start(args, format);
    if(0 > vasprintf(&string, format, args)) string = NULL;    //this is for logging, so failed allocation is not fatal
    va_end(args);

    if(string) {
        log_out(string);
        free(string);
    } else {
        log_out("Error while logging a message: Memory allocation failed.\n");
    }
}


답변

printf 스타일 형식을 사용하여 만든 문자열을 간단한 문자열을 사용하는 이미 가지고있는 함수에 쉽게 전달할 수 있기를 원하는 것처럼 들립니다. 다음을 사용하여 래퍼 함수를 만들 수 있습니다 stdarg.h시설을 vsnprintf()(컴파일러 / 플랫폼에 따라 쉽게 사용하지 못할 수도 있습니다) :

#include <stdarg.h>
#include <stdio.h>

// a function that accepts a string:

void foo( char* s);

// You'd like to call a function that takes a format string 
//  and then calls foo():

void foofmt( char* fmt, ...)
{
    char buf[100];     // this should really be sized appropriately
                       // possibly in response to a call to vsnprintf()
    va_list vl;
    va_start(vl, fmt);

    vsnprintf( buf, sizeof( buf), fmt, vl);

    va_end( vl);

    foo( buf);
}



int main()
{
    int val = 42;

    foofmt( "Some value: %d\n", val);
    return 0;
}

snprintf()루틴 제품군의 좋은 구현 (또는 구현)을 제공하지 않는 플랫폼의 경우 Holger Weiss 의 거의 공개 도메인 snprintf() 성공적으로 사용 했습니다 .


답변

에 대한 코드가 있으면 log_out()다시 작성하십시오. 대부분 다음을 수행 할 수 있습니다.

static FILE *logfp = ...;

void log_out(const char *fmt, ...)
{
    va_list args;

    va_start(args, fmt);
    vfprintf(logfp, fmt, args);
    va_end(args);
}

추가 로깅 정보가 필요한 경우 표시되는 메시지 전후에 인쇄 할 수 있습니다. 이렇게하면 메모리 할당 및 모호한 버퍼 크기 등이 절약됩니다. 아마도 logfp0 (널 포인터) 으로 초기화 하고 그것이 널인지 확인하고 적절하게 로그 파일을 열어야 할 log_out()필요가 있습니다. 그러나 기존의 코드는 어쨌든 그것을 처리해야합니다.

이 솔루션의 장점은 마치 변형 된 것처럼 간단히 호출 할 수 있다는 것입니다 printf(). 실제로 printf().

에 대한 코드가없는 경우 log_out()위에 설명 된 것과 같은 변형으로 대체 할 수 있는지 고려하십시오. 동일한 이름을 사용할 수 있는지 여부는 애플리케이션 프레임 워크와 현재 log_out()함수 의 궁극적 인 소스에 따라 다릅니다 . 다른 필수 기능과 동일한 개체 파일에있는 경우 새 이름을 사용해야합니다. 정확히 복제하는 방법을 찾을 수 없다면 적절한 양의 메모리를 할당하는 다른 답변에 제공된 것과 같은 변형을 사용해야합니다.

void log_out_wrapper(const char *fmt, ...)
{
    va_list args;
    size_t  len;
    char   *space;

    va_start(args, fmt);
    len = vsnprintf(0, 0, fmt, args);
    va_end(args);
    if ((space = malloc(len + 1)) != 0)
    {
         va_start(args, fmt);
         vsnprintf(space, len+1, fmt, args);
         va_end(args);
         log_out(space);
         free(space);
    }
    /* else - what to do if memory allocation fails? */
}

당연히 log_out_wrapper()대신에 log_out()-를 호출 하지만 메모리 할당 등은 한 번 수행됩니다. 나는 하나의 불필요한 바이트만큼 공간을 초과 할당 할 권리를 보유합니다. 반환 된 길이 vsnprintf()에 종료 널이 포함 되는지 여부를 다시 확인 하지 않았습니다.


답변

sprintf를 사용하지 마십시오.
String-Buffer가 오버플로되고 프로그램이 충돌합니다.
항상 snprintf 사용


답변

저는 이것을하지 않았으므로 정답을 가리킬 것입니다.

C에는 <stdarg.h>헤더를 사용하여 지정되지 않은 수의 피연산자를 취하는 함수에 대한 조항이 있습니다 . 함수를로 정의 하고 함수 내부를 void log_out(const char *fmt, ...);가져올 수 va_list있습니다. 그런 다음 메모리를 할당 vsprintf()하고 할당 된 메모리, 포맷 및 va_list.

또는이를 사용하여 sprintf()메모리를 할당하고 형식화 된 문자열을 반환하는 것과 유사한 함수를 작성하여 위와 같이 다소간 생성 할 수 있습니다. 메모리 누수가 발생할 수 있지만 로그 아웃하는 경우 중요하지 않을 수 있습니다.


답변

http://www.gnu.org/software/hello/manual/libc/Variable-Arguments-Output.html 은 stderr로 인쇄하는 다음 예제를 제공합니다. 대신 로그 기능을 사용하도록 수정할 수 있습니다.

 #include <stdio.h>
 #include <stdarg.h>

 void
 eprintf (const char *template, ...)
 {
   va_list ap;
   extern char *program_invocation_short_name;

   fprintf (stderr, "%s: ", program_invocation_short_name);
   va_start (ap, template);
   vfprintf (stderr, template, ap);
   va_end (ap);
 }

vfprintf 대신 인쇄 할 적절한 버퍼를 제공해야하는 곳에 vsprintf를 사용해야합니다.