2017-07-06 58 views
7

是否有可用於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

回答

1

在代碼成功之前可以繼續嘗試更大的緩衝區(或者認爲這太多了)。下面使用一個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