C相当于Python格式化方法
C equivalent of Python format method
让我说一开始我完全知道C中的sprintf
和printf
,但它们不符合我的需要。
我想要的是一个类似于return格式字符串的函数,它的参数就像printf
一样。例如。:
char *formatted = format("%c%s Mund%c!", '¡', "Hola", 'o');
C有这样的内置函数吗?还是应该手动实施?
如果是后者,如何实现这样的功能?
值得注意的是:
- 字符串长度未知
- 我不想打印字符串
附带说明:我不会使用 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;
}
让我说一开始我完全知道C中的sprintf
和printf
,但它们不符合我的需要。
我想要的是一个类似于return格式字符串的函数,它的参数就像printf
一样。例如。:
char *formatted = format("%c%s Mund%c!", '¡', "Hola", 'o');
C有这样的内置函数吗?还是应该手动实施? 如果是后者,如何实现这样的功能?
值得注意的是:
- 字符串长度未知
- 我不想打印字符串
附带说明:我不会使用 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);
尝试打印到本地 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;
}