如何从std::vector<char>构建一个std::string?

141

除了首先构建C风格的字符串,然后使用它来创建std::string之外,是否有更快/替代/“更好”的方法可以从字符向量初始化一个字符串?


6
这可以在不复制的情况下完成吗?换句话说,需要一个与std::vector<char> v2(std::move(v))执行相同操作的代码,但是新对象是一个std::string - tobi_s
8个回答

237

好的,最好的方法是使用以下构造函数:

template<class InputIterator> string (InputIterator begin, InputIterator end);

这将会导致类似于以下的结果:

std::vector<char> v;
std::string str(v.begin(), v.end());

49

我认为你可以这样做

std::string s( MyVector.begin(), MyVector.end() );

其中 MyVector 是您的 std::vector。


2
除了在VS2013中,它会在运行时关于无效迭代器进行断言,除非你设置_ITERATOR_DEBUG_LEVEL=1(在这种情况下,它似乎可以正常工作)。 - Cameron

44

使用C++11,您可以执行std::string(v.data())或者,如果您的向量末尾没有包含'\0',则执行std::string(v.data(), v.size())


即使使用C++98,我相信你可以使用std::string(&v[0])。当然,前提是该向量以空字符结尾。 - Jamie
2
@Jamie: 顺便说一下,在C++98中, string(&v[0], v.size()) 也应该可以工作,但是只有在 assert(not v.empty()); 后才能使用,因为如果向量为空,则 v[0]v.front() 都会引发未定义的行为。除了语法上不需要使用地址运算符的简单之外,这就是C++11的 data() 函数的真正好处,即使对于空向量也能正常工作。 - Adam H. Peterson
非常正确。不幸的是,我的项目在可预见的未来将停留在Visual Studio 2008上。首先检查向量长度是正确的。 - Jamie
1
如果向量中不包含 '\0',std::string(v.data()) 可能会导致更长的字符串。因此不要使用这种方法。 - heLomaN
@heLomaN:使用std::string(v.data(), v.size())有什么问题?答案中明确提到了这个方法。 - Sz.

12
std::string s(v.begin(), v.end());

v可以是任何可迭代的东西。(具体来说,begin()和end()必须返回InputIterators。)


7
我喜欢Stefan(2013年9月11日)的回答,但我想让它更加强调:
如果向量以空终止符结尾,则不应使用(v.begin(),v.end()):您应该使用v.data()(或对于C ++ 17之前的代码,“&v [0]”)。
如果v没有空终止符,则应使用(v.begin(),v.end())。
如果您使用begin()和end()而向量确实具有终止零,则最终会得到一个长度为4的字符串"abc\0",但实际上应该只是"abc"。

关于空终止符,你提出了非常好的观点,我曾经掉进了那个陷阱。 - Armut
(v.begin(), v.end())(v.data()) 之间有性能差异吗?后者会是 O(n) 吗? - 김선달

4

为了完整起见,另一种方法是std::string(&v[0])(虽然您需要确保字符串以空字符结尾,并且通常应优先使用std::string(v.data()))。

区别在于您可以使用前一种技术将向修改缓冲区的函数传递向量,而不能使用.data()。


为什么不能使用 data() 修改缓冲区?在我看来,如果你在非 const 的向量上调用 data(),它将返回一个 T *(如果你的向量是 const,'v[0]无论如何都会返回一个T const &`)。 - Adam H. Peterson
@AdamH.Peterson 看起来这似乎是标准中的疏忽。Std::vector 的 data 函数有 const 和非 const 两个版本,而在当前标准中,std::string 只有一个 const 版本:http://en.cppreference.com/w/cpp/string/basic_string/data 请注意,C++17 添加了一个非 const 版本,因此这可能不会一直是这种情况 - 然而,当前的标准规定:“修改通过 data 访问的字符数组的行为未定义。” - Riot
如果 v 是一个字符串,那么这将是正确的,但目标类型是一个字符串而 v 是一个向量。(但很高兴看到C++17将为字符串和向量提供相同的功能。) - Adam H. Peterson
@AdamH.Peterson 当然,你是对的。问题在一段时间前已经得到了回答,我假设整个问题都是关于字符串的,而没有进行检查。 - Riot

1
仅供代码演示:
    std::vector<char> vc {'l', 'o', 'v', 'e', '\0', '\0', '\0'};
    std::string sCat(vc.cbegin(), vc.cend());   
    std::cout << sCat << "|---" << '\n';
    std::cout << sCat.size() << "|---" << '\n';
    std::cout << "------------" << '\n';
    std::string sCat2(vc.data());
    std::cout << sCat2 << "|---" << '\n';
    std::cout << sCat2.size() << "|---" << '\n';
    std::cout << "------------" << '\n';
    std::string sCat3(&vc[0]);
    std::cout << sCat3 << "|---" << '\n';
    std::cout << sCat3.size() << "|---" << '\n';
    std::cout << "------------" << '\n';

输出:

love|---
7|---
------------
love|---
4|---
------------
love|---
4|---
------------

0
vector<char> vec;
//fill the vector;
std::string s(vec.begin(), vec.end());

你能在代码中加入一些解释吗? - Paul Floyd
1
C++11的字符串库模板如下:默认构造函数 (1) string();复制构造函数 (2) string(const string& str);子串构造函数 (3) string(const string& str, size_t pos, size_t len = npos);从C字符串构造函数 (4) string(const char* s);从缓冲区构造函数 (5) string(const char* s, size_t n);填充构造函数 (6) string(size_t n, char c);范围构造函数 (7) template <class InputIterator> string(InputIterator first, InputIterator last);初始化列表构造函数 (8) string(initializer_list<char> il);移动构造函数 (9) string(string&& str) noexcept;我们使用第七个模板。 - TechCat
嗨@TechCat!在回答下一个问题之前,请阅读有关撰写良好答案的内容。享受您在SO的逗留! - Diggy.
你能否编辑你的回答并加上描述呢? - Paul Floyd
不确定为什么这个回答被踩了,这是一个完全可以接受的答案。 - Jamie Nicholl-Shelley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接