【问题标题】:c++ Function to format time_t as std::string: buffer length?c ++函数将time_t格式化为std :: string:缓冲区长度?
【发布时间】:2011-12-17 15:22:51
【问题描述】:

我想要一个函数,该函数将采用 time_t 参数和任意格式字符串并对其进行格式化。我想要这样的东西:

std::string GetTimeAsString(std::string formatString, time_t theTime)
{
    struct tm *timeinfo;
    timeinfo = localtime( &theTime);

    char buffer[100];
    strftime(buffer, 100, formatString.c_str(), timeinfo);
    std::string result(buffer);
    return result;
}

但是我遇到的一个问题是缓冲区长度。我正在考虑使用 formatString * 4 作为缓冲区长度。但我猜你不能动态设置缓冲区长度?也许我可以选择一个任意大的缓冲区?我有点不知道如何使它通用。

如何编写函数来实现这一点?

【问题讨论】:

    标签: c++ time buffer stdstring strftime


    【解决方案1】:

    使用vector<char> 作为缓冲区而不是数组。反复增加大小,直到strftime 返回非零。

    【讨论】:

    • 或者,因为他返回的是一个字符串,所以使用这个字符串作为你的缓冲区。
    • 只有你有 C++11,@Mooing。
    • 没错,它只在 C++11 中有效。否则 vector 是更好的选择。
    【解决方案2】:

    我认为您最好的选择是提供一个固定缓冲区,它可能处理绝大多数情况,然后对其余情况进行特殊处理。类似的东西(未经测试,除了我头骨内的湿器):

    std::string GetTimeAsString (std::string formatString, time_t theTime) {
        struct tm *timeinfo;
        char buffer[100], *pBuff = buffer;
        int rc, buffSize = 100;
    
        timeinfo = localtime (&theTime);
        rc = strftime(pBuff, 100, formatString.c_str(), timeinfo);
    
        // Most times, we shouldn't enter this loop.
    
        while (rc == 0) {
            // Free previous in it was allocated.
    
            if (pBuff != buffer)
                delete[] pBuff;
    
            // Try with larger buffer.
    
            buffSize += 100;
            pBuff = new char [buffSize];
            rc = strftime(pBuff, buffSize, formatString.c_str(), timeinfo);
        }
    
        // Make string then free buffer if it was allocated.
    
        std::string result(pBuff);
        if (pBuff != buffer)
            delete[] pBuff;
    
        return result;
    }
    

    如果提供的缓冲区不够大,strftime 将返回零。在这种情况下,您开始分配更大的缓冲区,直到合适为止。

    您的未分配缓冲区大小和用于分配大小的增量可以根据您的需要进行调整。这种方法的优点是除了极少数情况外,您不会注意到效率损失(无论它可能多么小) - 绝大多数情况下都不会进行分配。

    此外,您可以选择其他方法(例如,+10%、加倍等)来增加缓冲区大小。

    【讨论】:

    • 或者,因为他返回一个字符串,所以使用这个字符串作为你的缓冲区。没有速度损失,而且代码更小。
    【解决方案3】:

    如果缓冲区的大小太小而无法容纳预期的结果,则 strftime() 函数将返回 0。使用此属性,您可以在堆上分配缓冲区并尝试 2 的连续幂作为其大小:1、2、4、8、16 等,直到缓冲区足够大。使用 2 的幂的优点是解决方案的复杂性与结果的长度成对数比例。

    还有一种特殊情况需要考虑:格式可能使得结果的大小始终为 0(例如,空格式)。不知道如何处理。

    【讨论】:

      【解决方案4】:

      如果你有 C++11:

      std::string GetTimeAsString(std::string formatString, time_t theTime)
      {
          struct tm *timeinfo;
          timeinfo = localtime( &theTime);
      
          formatString += '\a'; //force at least one character in the result
          std::string buffer;
          buffer.resize(formatstring.size());
          int len = strftime(&buffer[0], buffer.size(), formatString.c_str(), timeinfo);
          while (len == 0) {
              buffer.resize(buffer.size()*2);
              len = strftime(&buffer[0], buffer.size(), formatString.c_str(), timeinfo);
          } 
          buffer.resize(len-1); //remove that trailing '\a'
          return buffer;
      }
      

      注意,我将 formatString 作为 const 引用(为了速度和安全),并使用结果字符串作为缓冲区,这比稍后进行额外的复制要快。我也从与格式字符串相同的大小开始,每次尝试将大小加倍,但这很容易更改为更适合 strftime 的结果。

      【讨论】:

      • 这真的合法吗?我只是问,因为我最近不得不调查oper[],它使用cplusplus.com/reference/string/string/data - 这表明“返回的数组指向不应直接在程序中修改的内部位置”。
      • @Paxdiablo:对于data,这是正确的。但是,在 C++11 中:§ 21.4.1/5 For any basic_string object s, the identity &*(s.begin() + n) == &*s.begin() + n shall hold for all values of n such that 0 <= n < s.size(). 和 § 21.4.5/2 reference operator[](size_type pos) noexcept; ... Returns: *(begin() + pos) if pos < size() 从技术上讲,我所做的对 &buffer[0] 是合法的,但对于 buffer.c_str()buffer.data() 来说是合法的
      • 只要格式化的字符串实际包含字符,这将正常工作。不幸的是,某些格式参数(例如%p)可能会产生一个空字符串(取决于语言环境),这与错误无法区分,在这种情况下您将获得一个无限循环。到目前为止,我发现的唯一解决方法是在格式字符串的末尾添加一个任意字符(例如空格),以便区分空结果和错误,并在返回字符串之前删除该杂散字符。
      • @syam:cplusplus 和 cppreference 在返回类型上存在分歧。由于 cppreference 在历史上更准确,我会相信你。固定的。 (从技术上讲,它不会是一个无限循环,它会抛出std::bad_alloc,但你的观点是正确的)
      • @MooingDuck 好吧,我没有检查任何一个站点,老实说,我是从 man 3 strftime 得到的,这也是一个非常安全的选择。 ;) 至于无限循环,确实是我的错误措辞。
      【解决方案5】:

      使用std::put_time()的C++11解决方案:

      std::string GetTimeAsString(std::string formatString, time_t theTime)
      {
          struct tm* timeinfo = localtime(&theTime);
      
          std::ostringstream os;
          os << std::put_time(timeinfo, formatString.c_str());
          return os.str();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-29
        • 1970-01-01
        • 1970-01-01
        • 2014-11-28
        • 2014-05-14
        • 2013-06-16
        • 2011-09-23
        相关资源
        最近更新 更多