使用类似 strndup 的语义从 char[] 创建一个 std::string

Create a std::string from char[] with strndup like semantics

我有一个

char txt_msg[80];

该数组最多可包含 80 个字符,例如不能保证有终止空值。但是,如果少于 80 个字符,则会有一个终止空值。

现在我正在使用它从中获取 std::string:

std::string(txt_msg, txt_msg + ::strnlen(txt_msg, sizeof(txt_msg)));

创建一个 C++ 字符串,这看起来有点令人反感。有没有更多的 C++y 方法来做到这一点?

Is there a more C++y way to do that?

std::string 的构造而言,并非如此。虽然,至少,因为你已经知道 char[] 的最大长度,你可以使用 std::string(const char*, size_type) 构造函数而不是 std::string(InputIt, InputIt) 构造函数,因此构造函数可以避免计算长度:

std::string(txt_msg, ::strnlen(txt_msg, sizeof(txt_msg));

由于 strnlen() 是一个非标准的 POSIX 扩展,如果需要的话,编写一个手动实现并不难:

#include <algorithm>

size_t strnlen(const char *s, size_t maxlen)
{
    const char *s_end = s + maxlen;
    const char *found = std::find(s, s_end, '[=11=]');
    return (found != s_end) ? size_t(found - s) : maxlen;
}

也就是说,解决您的问题的 C++ 解决方案是将 std::string 构造包装在辅助模板函数中,例如:

template<size_t N>
std::string to_string(const char (&arr)[N])
{
    return std::string(arr, strnlen(arr, N));
}

然后你可以在需要的时候这样做:

char txt_msg[80];
...
std::string s = to_string(txt_msg);

而不是这样做:

char txt_msg[80];
...
std::string s = std::string(txt_msg, txt_msg + strnlen(txt_msg, sizeof(txt_msg)));
//or
std::string s = std::string(txt_msg, strnlen(txt_msg, sizeof(txt_msg)));

我可能会做这样的事情:

char txt_msg[80];

auto s = std::string(std::begin(txt_msg), std::find(std::begin(txt_msg), std::end(txt_msg), '[=10=]'));

std::find 将 return 第一个 空终止符 或数组末尾的位置。

也许您应该考虑使用 std::string_view - 一种非拥有的类似字符串的引用类型,它可以像 std::string 一样使用;在您的情况下,它将由您的消息数组支持:

auto sv = std::string_view{txt_msg, ::strnlen(txt_msg, std::extent_v<decltype(txt_msg)>};

但这确实仍然很不确定,并且严重破坏了 DRY principle:重复 3 次。那么,我们写一些实用函数怎么样? :

inline std::string_view 
constrain_by_nul(std::string_view sv) {
    return sv.substr(0, sv.find('[=11=]'));
}

有了这个,你可以写:

auto sv = constrain_by_nul(std::string_view{txt_msg, std::size(txt_msg)});

更好,但还不够:我们提到 txt_msg 两次。不幸的是,我们不能直接从容器 (IIANM) 构造字符串视图。那么也许是另一个效用函数?

template<typename CharT, std::size_t N> 
std::basic_string_view<CharT>
inline make_string_view(CharT (&arr)[N]) { 
    return {arr, N};
};

现在你可以写:

auto sv = constrain_by_nul(make_string_view(txt_msg));

这正是您最初想要做的。通过适当的编译器优化,它实际上可能会编译成同样的东西。并且 - 没有复制也没有堆分配,因为它不是 std::string.


在这个 SO 问题中阅读有关字符串视图的更多信息:What is string_view?