strftime的合理snprintf样的替代品?

问题描述:

是否有可用于strftime的合理snprintf样的替代品?

  1. 任何标准(C,C++,POSIX,Linux的...)替代strftime计算串缓冲器的所需尺寸对于给定的格式和时间,
  2. 截断输出(而不是像strftime那样未定义数组内容),如果缓冲区大小小于完整输出所需的大小。

例如,snprintf类似于接受strftime格式字符串的日期/时间格式的语义。

在C++ 11及更高版本中,像std::put_time这样的函数不是一个选项,因为这些函数可能会试图动态分配额外的内存并可能会抛出异常。

+2

假设有人会在接下来的7983年更新您的代码,然后计算您需要的格式化时间在您的手指上的字节数。似乎没有必要自动执行此操作。避免长周和月名,因为它们依赖于语言环境。 – Art

+0

不,没有,但你可以相对容易地写出你自己的。 ['stdarg.h'](http://www.cplusplus.com/reference/cstdarg/)和['vsnprintf'](http://www.cplusplus.com/reference/cstdio/vsnprintf/)可以有助于实现这一点。 – Akira

+1

@Akira根据当前的语言环境等等,对于星期几和月份名称来说可能并不那么容易。stdarg和vsnprintf将不需要替换,因为strftime具有固定数量的参数。 – Ctx

在代码成功之前可以继续尝试更大的缓冲区(或者认为这太多了)。下面使用一个VLA(不是C++),来偷偷地避免“试图动态分配额外的内存” - 眨眼。

简单地分配一个大缓冲区,比如char buf[J_STRFTIME_MAX],对于实际编码来说应该是足够的。 @Michaël Roy并避免迭代方法。

#include <stdio.h> 
#include <time.h> 
#define J_STRFTIME_MAX 100 

size_t j_strftime(char * s, size_t maxsize, const char * fmt, const struct tm * t) { 
    size_t sz = strftime(s, maxsize, fmt, t); 
    if (sz) { 
    return sz; 
    } 
    size_t new_size = maxsize ? maxsize : 1; 
    do { 
    new_size *= 2; 
    char new_s[new_size]; 
    sz = strftime(new_s, sizeof new_s, fmt, t); 
    if (sz) { 
     s[0] = 0; 
     // strncat(s, new_s, maxsize); 
     strncat(s, new_s, maxsize - 1); 
     return strlen(new_s); 
    } 
    } while (sz < J_STRFTIME_MAX/2); 
    return 0; 
} 

int main() { 
    time_t now; 
    time(&now); 
    struct tm tm = *gmtime(&now); 
    for (size_t i = 1; i < 30; i += 3) { 
    char s[i]; 
    size_t sz = j_strftime(s, sizeof s, "%c", &tm); 
    printf("%2zu %2zu <%s>\n", i, sz, s); 
    } 
} 

输出

1 24 <T> 
4 24 <Thu > 
7 24 <Thu Jul> 
10 24 <Thu Jul 6> 
13 24 <Thu Jul 6 14> 
16 24 <Thu Jul 6 14:45> 
19 24 <Thu Jul 6 14:45:00> 
22 24 <Thu Jul 6 14:45:00 20> 
25 24 <Thu Jul 6 14:45:00 2017> 
28 24 <Thu Jul 6 14:45:00 2017> 

非迭代

size_t j_strftime2(char * s, size_t maxsize, const char * fmt, const struct tm * t) { 
    size_t sz = strftime(s, maxsize, fmt, t); 
    if (sz == 0) { 
    char new_s[J_STRFTIME_MAX]; 
    sz = strftime(new_s, sizeof new_s, fmt, t); 
    if (sz == 0) { 
     return 0; // Too too big 
    } 
    s[0] = 0; 
    // strncat(s, new_s, maxsize); 
    strncat(s, new_s, maxsize - 1); 
    } 
    return sz; 
} 

[编辑]码纠正。

+0

为了避免不必要的迭代,你可以从更大的'new_size'开始:size_t new_size = maxsize> 128? maxsize:128;' – chqrlie

+0

'strftime'可能由于目标缓冲区大小之外的其他原因而失败,在这种情况下,您的方法肯定会有未定义的行为。 – chqrlie

+0

@chqrlie真的。一个很好的直接方法。然而,OP似乎对内存使用情况有隐含的担忧,所以迭代式方法不断涌现。 – chux