【问题标题】:copying from a std::string to a char array and null-terminating the result从 std::string 复制到 char 数组并以空值结尾的结果
【发布时间】:2012-06-05 18:54:50
【问题描述】:

我有一个 Visual Studio 2008 C++03 应用程序,我想从 std::string 复制到 char 数组,但我需要 char 数组以空终止,即使它必须截断字符串才能这样做.

例如,这可以按需要工作:

inline void CopyAndNullTerminate( const std::string& source, 
                                  char* dest, 
                                  size_t dest_size )
{
    source.copy( dest, dest_size );
    using std::min;
    char* end = dest + min( dest_size - 1, source.size() );
    *end = '\0';
}

int main()
{
    enum{ MAX_STRING_SIZE = 15 };
    char dest[ MAX_STRING_SIZE ];
    std::string test = "This is a test";

    CopyAndNullTerminate( test, dest, MAX_STRING_SIZE );

    assert( strcmp( test.c_str(), dest ) == 0 );
    return 0;
}

示例:http://ideone.com/0EBYb

有没有更短、更有效的方法来做到这一点?

谢谢

【问题讨论】:

    标签: c++ string


    【解决方案1】:

    是的,使用strncpy,在cstring中定义:

    void copyString(const std::string& input, char *dst, size_t dst_size)
    {
        strncpy(dst, input.c_str(), dst_size - 1);
        dst[dst_size - 1] = '\0';
    }
    

    请注意,对于std::string 的某些实现(正如@K-ballo 所指出的),这可能更短,但效率较低。这是因为 std::string 不能保证使用 C 样式字符串实现,尽管在大多数情况下可能是这种情况。

    【讨论】:

    • 这有一个不幸的副作用,即必须为那些不使用它作为内部表示的std::string 的实现构造一个有效的以空结尾的 C 字符串。
    • @K-ballo 是的,但对于大多数实现,我认为这将是使用的实现......
    • 这可能效率低下的真正原因是它用空值填充缓冲区,即使只有一个是必要的。
    • @PaulH 不,这是必要的,因为如果strncpy 已达到分配大小的末尾,它不会自动附加\0
    • @RichardJ.RossIII - strncpy 在我的测试中似乎是 std::string::copy 的两倍。所以,你赢了!
    【解决方案2】:

    假设 dest_size 保证至少为 1(这对我来说似乎是合理的,因为否则无法将任何内容复制和 null 终止到缓冲区中):

    inline void CopyAndNullTerminate( const std::string& source, 
                                      char* dest, 
                                      size_t dest_size )
    {
        dest[source.copy(dest, dest_size-1)] = 0;
    }
    

    在 C++11 和所有主动维护的 C++03 实现(包括 Visual Studio)中,std::string 具有连续存储,就像 std::vector。因此,您可以使用memcpy 而不是std::string::copy 并比较性能。

    同样,您可以比较strncpystd::copystd::copy_nstrcpy_s,以及可能我忘记的其他人,看看哪个是最佳优化的。在每种情况下,您都可以将要复制的字节数计算为std::min(source.size(), dest_size-1)。这也避免了strncpy 最低效的情况(将小字符串复制到大缓冲区中)。

    在执行所有这些操作时,您可以相信即使source 是一个空字符串,调用source[0] 也是有效的

    【讨论】:

      【解决方案3】:

      如果你首先初始化你的目标字符串(为 null),那么如果你不覆盖数组中的最后一个元素,你可以得到它将被 null 终止。

      enum{ MAX_STRING_SIZE = 15 };
      
      char dest[ MAX_STRING_SIZE ] = {0}; // all elements are initialized to 0
      std::string test = "This is a test";
      
      // determine the length to copy, it will be the min of MAX_STRING_SIZE-1 
      // or test.size.  This will ensure that even if the string it shorter than
      // the MAX_STRING_SIZE, that it will copy properly -- without reading past the
      // array bounds of the string.
      auto copy_len = std::min(test.size(), MAX_STRING_SIZE - 1);
      
      std::copy(
         test.begin(),
         test.begin() + copy_len,
         dest);
      

      这会将最多 14 个字符复制到您的dest 对象。如果测试字符串少于 14 个字符,它只会复制测试字符串的长度。所有未复制的元素都将保持其初始化值(null)。

      【讨论】:

        【解决方案4】:

        这里有几个较短的:

        inline void CopyAndNullTerminate( const std::string& source, 
                                          char* dest, 
                                          size_t dest_size )
        {
            *dest = '\0'; // assumes `dest_size > 0`
            strncat(dest, source, dest_size);
        }
        
        void CopyAndNullTerminate(std::string const &source,
                                  char *dest,
                                  size_t dest_size) { 
            sprintf(dest, "%*s", dest_size, source.c_str());
        }
        

        效率可能会引发更多问题,但我认为没有任何理由相信两者都会特别慢。

        【讨论】:

          【解决方案5】:

          好的 ole' C strcpy_s 怎么样?

          strcpy_s(dest, MAX_STRING_SIZE, test.c_str());
          

          比起strncpy,我更喜欢这个。 strcpy_s(和它的老表亲 strlcpy)复制循环将在复制 '\0' 后终止,但 strncpy 将始终写出指定的字节数(如果源字符串为更短)。

          【讨论】:

          • 不幸的是,strlcpy 不是 C 标准的一部分,据我所知,它是一个 BSD 函数,并且在 VS 2008 中不可用。
          • @RichardJ.RossIII,顺便说一句,我更喜欢这些函数的原因是strncpy 总是写出第三个参数中指定的字节数,如果源字符串为比指定的字节长。
          • @user315052 - _s 函数不会静默截断。相反,他们会因不快乐而爆发。
          猜你喜欢
          • 2022-01-05
          • 2012-01-10
          • 1970-01-01
          • 2015-11-07
          • 2011-10-26
          • 2011-08-29
          • 2012-05-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多