C相当于Python格式化方法

C equivalent of Python format method

让我说一开始我完全知道C中的sprintfprintf,但它们不符合我的需要。

我想要的是一个类似于return格式字符串的函数,它的参数就像printf一样。例如。: char *formatted = format("%c%s Mund%c!", '¡', "Hola", 'o');

C有这样的内置函数吗?还是应该手动实施? 如果是后者,如何实现这样的功能?

值得注意的是:

  1. 字符串长度未知
  2. 我不想打印字符串

附带说明:我不会使用 c++,我会使用 mingw-64 gcc

没有等效函数,除非您自己创建一个,因为与 python 不同,C 中的字符串是简单数组, 负责分配你需要多少内存,传递一个指向函数的指针,然后释放它。这就是为什么在像 sprintf 这样的函数中你需要指定一个输出数组(并且在像 snprintf 这样的变体中可以选择一个 size 值)。

自定义实现应该是这样的(为了简单起见,不包括错误检查):

#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#define STRSIZE 256

char* my_sprintf(const char* fmt, ...) {
    /* Create a temporary buffer */
    char temp[STRSIZE];
    /* Get variadic arguments */
    va_list args;
    va_start(args, fmt);
    /* Use the variadic argument variant of snprintf
     * The return value is the number of characters that would have been written,
     * if the buffer was sufficiently large. We use this to determine if we
     * need to repeat on a larger buffer to account for strings of any length */
    int len = vsnprintf(result, STRSIZE, fmt, args);
    /* Cleanup */
    va_end(args);
    /* If the entire string fits in the temp buffer, return a copy */
    if (len < STRSIZE)
        return strdup(temp);
    /* Otherwise, allocate enough memory and repeat */
    char* result = (char*)malloc(len + 1); // +1 for the null terminator
    /* The variadic argument pack is consumed already, so recreate it */
    va_start(args, fmt);
    /* Use the variadic argument variant of sprintf
     * (we already have enough allocated memory now, so no need for snprintf) */
    vsprintf(result, fmt, args);
    /* Cleanup */
    va_end(args);
    return result;
}

完成后,别忘了释放返回的指针!

char* my_string = my_sprintf("My %s", "format");
...
free(my_string);

您遇到以下问题:

'¡'

这是不是一个char。是占两个char的UTF-8字符。在十六进制中,它是:

'\xC2\xA1'

或者,取决于字节顺序:

'\xA1\xC2'

%c 格式将 处理得很好。你想使用双引号所以它是一个 string 并使用 %s.


简单的方法就是向下传递缓冲区:

编辑:调整以添加缓冲区长度。

char *
generate(char *buf,size_t siz)
{

    snprintf(buf,siz,"%s%s Mund%c!","¡", "Hola", 'o');
    buf[siz - 1] = 0;

    return buf;
}

更接近你想要的是分配结果:

char *
generate(void)
{
    char buf[1000];

    snprintf(buf,sizeof(buf),"%s%s Mund%c!","¡", "Hola", 'o');
    buf[sizeof(buf) - 1] = 0;

    char *format = strdup(buf);

    return format;
}

请注意 c 没有 有自动 free/garbage 收集,所以对于第二个例子, caller 必须做(例如):

char *formatted = generate();

// do stuff with formatted ...

// free the result when no longer needed ...
free(formatted);

使用 clean-up 的方法。

尝试打印到本地 buf 并复制它。
否则分配到所需的报告大小。
沿途检查错误。

#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#define MY_APRINTF_BUF_SZ 256
#define MY_APRINTF_TRIES 3 // 2 or more

char* my_aprintf(const char *fmt, ...) {
  va_list args;
  char buf_local[MY_APRINTF_BUF_SZ];
  char *buf = buf_local;
  size_t buf_size = sizeof buf_local;

  // Try a few times.
  // Often the 1st time with a local buffer will be good enough for common sized strings
  // Otherwise it "should" work on the 2nd time.
  // Some corner cases may require more tries.
  // Give up after a while.
  for (int i = 0; i < MY_APRINTF_TRIES; i++) {
    va_start(args, fmt);  // TBD: Review if va_copy better/required
    int length_needed = vsnprintf(buf, buf_size, fmt, args);
    va_end(args);
    // If encoded error occurred ...
    if (length_needed < 0) {
      if (buf == buf_local) {
        return NULL;
      }
      break;
    }
    size_t size_needed = (unsigned) length_needed + 1u;

    if (size_needed <= buf_size) {
      if (buf == buf_local) { // First time
        buf = strdup(buf);  // Code your own my_strdup if strdup not available.
      }
      return buf;
    }
    if (buf == buf_local) {
      buf = NULL;
    }

    // Get more memory
    void *ptr = realloc(buf, size_needed);
    if (ptr == NULL) {
      break;
    }
    buf = ptr;
    buf_size = size_needed;
  }

  // Give up
  free(buf);
  return NULL;
}

示例用法

int main() {
  char *s = my_aprintf("%d %s\n", 42, "Hello world!");
  if (s) {
    puts(s);
    free(s);
  }
}

如果需要

#include <errno.h>
#include <stdlib.h>

// Use as needed
char *my_strdup(const char *buf) {
  size_t size = strlen(buf) + 1u;
  char *s = malloc(size);
  if (s) {
    memcpy(s, buf, size);
  } else {
#ifdef ENOMEM
    errno = ENOMEM;
#endif
  }
  return s;
 }