使用类似 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?
我有一个
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?