【问题标题】:Convert a vector<int> to a string将 vector<int> 转换为字符串
【发布时间】:2009-09-16 03:14:47
【问题描述】:

我有一个 vector&lt;int&gt; 容器,其中包含整数(例如 {1,2,3,4}),我想转换为格式为

的字符串
"1,2,3,4"

在 C++ 中最简洁的方法是什么? 在 Python 中,我会这样做:

>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'

【问题讨论】:

标签: c++ vector tostring


【解决方案1】:

绝对不如 Python 优雅,但没有什么比 C++ 中的 Python 优雅。

您可以使用stringstream ...

#include <sstream>
//...

std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
  if(i != 0)
    ss << ",";
  ss << v[i];
}
std::string s = ss.str();

您也可以改用std::for_each

【讨论】:

  • 我想你的意思是 array.size() 不是 v.size(),不是吗?
  • 不管向量叫什么。
  • 应该是std::string s = ss.str()。如果您想要const char*,请使用s.c_str()。 (请注意,虽然语法正确,ss.str().c_str() 会给你一个 const char* 指向一个临时的,它将在完整表达式的末尾不复存在。这很痛苦。)
  • 为什么不直接使用 string.append?
  • 答案不完整,没有#include &lt;sstream&gt;
【解决方案2】:

使用 std::for_each 和 lambda 你可以做一些有趣的事情。

#include <iostream>
#include <sstream>

int main()
{
     int  array[] = {1,2,3,4};
     std::for_each(std::begin(array), std::end(array),
                   [&std::cout, sep=' '](int x) mutable {
                       out << sep << x; sep=',';
                   });
}

请参阅this question 了解我写的一个小班。这不会打印尾随逗号。此外,如果我们假设 C++14 将继续为我们提供基于范围的算法等价物,如下所示:

namespace std {
   // I am assuming something like this in the C++14 standard
   // I have no idea if this is correct but it should be trivial to write if it  does not appear.
   template<typename C, typename I>
   void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;   
int main()
{
     int  array[] = {1,2,3,4};
     std::copy(array, POI(std::cout, ","));
  // ",".join(map(str,array))               // closer
}

【讨论】:

  • 我认为这并不完全等同于 Python 的 join - 它会在末尾插入一个额外的 ","。
  • 不等同,但同样优雅(实际上我认为更多,但这只是一种观点)。
  • 显然优雅是主观的。因此,如果您和另外两个人更喜欢更长、更重复但不起作用的代码,那么它会更优雅;-p
  • 您可以使用 string::substr 成员函数忽略最后的逗号。将 0 到 n-1 的子字符串分配给结果变量。
  • @SteveJessop:更好?
【解决方案3】:

您可以使用 std::accumulate。考虑下面的例子

if (v.empty() 
    return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
                     [](const std::string& a, int b){
                           return a + ',' + std::to_string(b);
                     });

【讨论】:

  • ',' 应该是","
  • @Matt string 类有一个+ 运算符的重载,它也可以接受字符。所以',' 就好了。
【解决方案4】:

另一种选择是使用std::copyostream_iterator 类:

#include <iterator>  // ostream_iterator
#include <sstream>   // ostringstream
#include <algorithm> // copy

std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);

也没有 Python 好。 为此,我创建了一个join 函数:

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  for (A it=begin;
       it!=end;
       it++)
  {
    if (!result.empty())
      result.append(t);
    result.append(*it);
  }
  return result;
}

然后这样使用:

std::string s=join(array.begin(), array.end(), std::string(","));

你可能会问我为什么要传入迭代器。嗯,其实我是想反转数组,所以我是这样使用的:

std::string s=join(array.rbegin(), array.rend(), std::string(","));

理想情况下,我希望模板化到可以推断 char 类型并使用字符串流的程度,但我还想不通。

【讨论】:

  • 既然评论太多了,我已经发布了一个答案(stackoverflow.com/questions/1430757/1432040#1432040),试图解决你最后一句话中给出的谜题。
  • 您的join 函数也可以与向量一起使用吗?请你举个例子,我是 C++ 新手。
  • 你可以将迭代器更改为答案中的预增量吗?
【解决方案5】:

使用 Boost 和 C++11 可以这样实现:

auto array = {1,2,3,4};
join(array | transformed(tostr), ",");

嗯,差不多。这是完整的示例:

#include <array>
#include <iostream>

#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>

int main() {
    using boost::algorithm::join;
    using boost::adaptors::transformed;
    auto tostr = static_cast<std::string(*)(int)>(std::to_string);

    auto array = {1,2,3,4};
    std::cout << join(array | transformed(tostr), ",") << std::endl;

    return 0;
}

归功于Praetorian

您可以像这样处理任何值类型:

template<class Container>
std::string join(Container const & container, std::string delimiter) {
  using boost::algorithm::join;
  using boost::adaptors::transformed;
  using value_type = typename Container::value_type;

  auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
  return join(container | transformed(tostr), delimiter);
};

【讨论】:

    【解决方案6】:

    这只是试图解决1800 INFORMATION's remark 给出的第二个解决方案缺乏通用性的谜团,而不是试图回答这个问题:

    template <class Str, class It>
    Str join(It begin, const It end, const Str &sep)
    {
      typedef typename Str::value_type     char_type;
      typedef typename Str::traits_type    traits_type;
      typedef typename Str::allocator_type allocator_type;
      typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
                                           ostringstream_type;
      ostringstream_type result;
    
      if(begin!=end)
        result << *begin++;
      while(begin!=end) {
        result << sep;
        result << *begin++;
      }
      return result.str();
    }
    

    在我的机器上工作 (TM)。

    【讨论】:

    • Visual Studio 2013 对 typedef 感到非常困惑。并不是说你在 2009 年就知道这一点。
    • @Jes:我现在已经盯着这个看了 5 分钟,但不知道 VS 可能会绊倒什么。你能说得更具体点吗?
    • 对不起,我想我曾尝试在没有
    • @Jes:这应该适用于任何可以流式传输的类型(即,operator&lt;&lt; 重载)。当然,没有operator&lt;&lt; 的类型可能会导致非常混乱的错误消息。
    • 不幸的是,这不能编译:join(v.begin(), v.end(), ",")。如果 sep 参数是字符串文字,则模板参数推导不会产生正确的结果。 My attempt at a solution for this issue。还提供更现代的基于范围的重载。
    【解决方案7】:

    很多模板/想法。我的没有那么通用或高效,但我只是遇到了同样的问题,想把它作为简短而甜蜜的东西加入到混合中。它以最短的行数获胜...... :)

    std::stringstream joinedValues;
    for (auto value: array)
    {
        joinedValues << value << ",";
    }
    //Strip off the trailing comma
    std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);
    

    【讨论】:

    • 感谢简单的代码。可能希望将其更改为“auto &”以避免额外的副本并获得一些简单的性能提升。
    • 不是使用substr(...),而是使用pop_back() 删除最后一个字符,这样会变得更加清晰和干净。
    【解决方案8】:
    string s;
    for (auto i : v)
        s += (s.empty() ? "" : ",") + to_string(i);
    

    【讨论】:

    • 欢迎来到 Stack Overflow!虽然这段代码可以解决问题,但最好为可能不理解这段代码的人添加详细说明并解释它是如何工作的。
    • 当前的最佳答案并不复杂,这是最小/最干净的工作答案。对于大型数组,效率不如std::stringstream,因为stringstream 将能够乐观地分配内存,从而导致对于大小为n 的数组的O(n.log(n)) 性能而不是O(n²)回答。此外,stringstream 可能不会为 to_string(i) 构建临时字符串。
    【解决方案9】:

    如果你想做std::cout &lt;&lt; join(myVector, ",") &lt;&lt; std::endl;,你可以这样做:

    template <typename C, typename T> class MyJoiner
    {
        C &c;
        T &s;
        MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
    public:
        template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
        template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
    };
    
    template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
    {
        auto i = mj.c.begin();
        if (i != mj.c.end())
        {
            o << *i++;
            while (i != mj.c.end())
            {
                o << mj.s << *i++;
            }
        }
    
        return o;
    }
    
    template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
    {
        return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
    }
    

    请注意,此解决方案直接连接到输出流而不是创建辅助缓冲区,并且适用于任何在 ostream 上具有运算符

    这也适用于 boost::algorithm::join() 失败的情况,当您有 vector&lt;char*&gt; 而不是 vector&lt;string&gt;

    【讨论】:

      【解决方案10】:

      我喜欢 1800 的回答。但是我会将第一次迭代移出循环,因为 if 语句的结果仅在第一次迭代后更改一次

      template <class T, class A>
      T join(const A &begin, const A &end, const T &t)
      {
        T result;
        A it = begin;
        if (it != end) 
        {
         result.append(*it);
         ++it;
        }
      
        for( ;
             it!=end;
             ++it)
        {
          result.append(t);
          result.append(*it);
        }
        return result;
      }
      

      如果您愿意,这当然可以减少为更少的语句:

      template <class T, class A>
      T join(const A &begin, const A &end, const T &t)
      {
        T result;
        A it = begin;
        if (it != end) 
         result.append(*it++);
      
        for( ; it!=end; ++it)
         result.append(t).append(*it);
        return result;
      }
      

      【讨论】:

      • 您不应该对未知的迭代器类型使用后增量。那可能很贵。 (当然,在处理字符串时,这可能不会有太大的不同。但是一旦你学会了这个习惯......)
      • 后增量没问题,只要您使用返回的临时值。例如“result.append(*it); ++it;”几乎总是和“result.append(*it++);”一样昂贵第二个有一个额外的迭代器副本。
      • 糟糕,我刚刚在 for 循环中发现了 post 增量。复制和粘贴错误。我已经修复了帖子。
      • @Ian:当我教 C++ 时,我强迫我的学生使用 ++i,除非他们真正需要 i++,因为这是他们不会忘记这一点的唯一方法。区别。 (顺便说一句,我也一样。)他们之前学过 Java,各种 C 主义都在流行,他们花了几个月的时间(每周 1 次讲座 + 实验室工作),但最后大部分他们学会了使用预增量的习惯。
      • @sbi:同意我也总是默认预增量,流氓后增量来自复制别人的 for 循环并更改它。在我的第一个回复中,我以为您担心的是“result.append(*it++)”而不是 for 循环。看到循环中的帖子增量,我有点尴尬。有些人似乎遵循了不要过度使用后增量的建议,即使在适当的时候也从不使用或更改它。不过我现在知道你不属于这一类。
      【解决方案11】:

      有一些有趣的尝试可以为问题提供优雅的解决方案。我有一个想法,使用模板流来有效地回答 OP 最初的困境。虽然这是一篇旧帖子,但我希望未来偶然发现此问题的用户会发现我的解决方案很有用。

      首先,一些答案(包括接受的答案)不会促进可重用性。由于 C++ 没有提供一种在标准库中连接字符串的优雅方法(我已经看到),因此创建一个灵活且可重用的方法变得很重要。这是我的镜头:

      // Replace with your namespace //
      namespace my {
          // Templated join which can be used on any combination of streams, iterators and base types //
          template <typename TStream, typename TIter, typename TSeperator>
          TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) {
              // A flag which, when true, has next iteration prepend our seperator to the stream //
              bool sep = false;                       
              // Begin iterating through our list //
              for (TIter i = begin; i != end; ++i) {
                  // If we need to prepend a seperator, do it //
                  if (sep) stream << seperator;
                  // Stream the next value held by our iterator //
                  stream << *i;
                  // Flag that next loops needs a seperator //
                  sep = true;
              }
              // As a convenience, we return a reference to the passed stream //
              return stream;
          }
      }
      

      现在要使用它,您可以简单地执行以下操作:

      // Load some data //
      std::vector<int> params;
      params.push_back(1);
      params.push_back(2);
      params.push_back(3);
      params.push_back(4);
      
      // Store and print our results to standard out //
      std::stringstream param_stream;
      std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl;
      
      // A quick and dirty way to print directly to standard out //
      my::join(std::cout, params.begin(), params.end(), ",") << std::endl;
      

      请注意,流的使用如何使这个解决方案变得非常灵活,因为我们可以将结果存储在字符串流中以便以后回收,或者我们可以直接写入标准输出、文件,甚至写入实现为溪流。打印的类型必须是可迭代的并且与源流兼容。 STL 提供了多种兼容多种类型的流。所以你真的可以带着这个去城里。在我的脑海中,您的向量可以是 int、float、double、string、unsigned int、SomeObject* 等。

      【讨论】:

        【解决方案12】:

        我创建了一个帮助头文件来添加扩展连接支持。

        只需将以下代码添加到您的通用头文件中,并在需要时包含它。

        用法示例:

        /* An example for a mapping function. */
        ostream&
        map_numbers(ostream& os, const void* payload, generic_primitive data)
        {
            static string names[] = {"Zero", "One", "Two", "Three", "Four"};
            os << names[data.as_int];
            const string* post = reinterpret_cast<const string*>(payload);
            if (post) {
                os << " " << *post;
            }
            return os;
        }
        
        int main() {
            int arr[] = {0,1,2,3,4};
            vector<int> vec(arr, arr + 5);
            cout << vec << endl; /* Outputs: '0 1 2 3 4' */
            cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */
            cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */
            cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */
            cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */
            string post = "Mississippi";
            cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */
            return 0;
        }
        

        幕后代码:

        #include <iostream>
        #include <vector>
        #include <list>
        #include <set>
        #include <unordered_set>
        using namespace std;
        
        #define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; }
        #define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T;
        
        typedef void* ptr;
        
        /** A union that could contain a primitive or void*,
         *    used for generic function pointers.
         * TODO: add more primitive types as needed.
         */
        struct generic_primitive {
            GENERIC_PRIMITIVE_CLASS_BUILDER(int);
            GENERIC_PRIMITIVE_CLASS_BUILDER(ptr);
            union {
                GENERIC_PRIMITIVE_TYPE_BUILDER(int);
                GENERIC_PRIMITIVE_TYPE_BUILDER(ptr);
            };
        };
        
        typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive);
        template<typename T>
        class Join {
        public:
            Join(const T& begin, const T& end,
                    const string& separator = " ",
                    mapping_funct_t mapping = 0,
                    const void* payload = 0):
                    m_begin(begin),
                    m_end(end),
                    m_separator(separator),
                    m_mapping(mapping),
                    m_payload(payload) {}
        
            ostream&
            apply(ostream& os) const
            {
                T begin = m_begin;
                T end = m_end;
                if (begin != end)
                    if (m_mapping) {
                        m_mapping(os, m_payload, *begin++);
                    } else {
                        os << *begin++;
                    }
                while (begin != end) {
                    os << m_separator;
                    if (m_mapping) {
                        m_mapping(os, m_payload, *begin++);
                    } else {
                        os << *begin++;
                    }
                }
                return os;
            }
        private:
            const T& m_begin;
            const T& m_end;
            const string m_separator;
            const mapping_funct_t m_mapping;
            const void* m_payload;
        };
        
        template <typename T>
        Join<T>
        join(const T& begin, const T& end,
             const string& separator = " ",
             ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0,
             const void* payload = 0)
        {
            return Join<T>(begin, end, separator, mapping, payload);
        }
        
        template<typename T>
        ostream&
        operator<<(ostream& os, const vector<T>& vec) {
            return join(vec.begin(), vec.end()).apply(os);
        }
        
        template<typename T>
        ostream&
        operator<<(ostream& os, const list<T>& lst) {
            return join(lst.begin(), lst.end()).apply(os);
        }
        
        template<typename T>
        ostream&
        operator<<(ostream& os, const set<T>& s) {
            return join(s.begin(), s.end()).apply(os);
        }
        
        template<typename T>
        ostream&
        operator<<(ostream& os, const Join<T>& vec) {
            return vec.apply(os);
        }
        

        【讨论】:

          【解决方案13】:

          这是一个通用的 C++11 解决方案,可以让您这样做

          int main() {
              vector<int> v {1,2,3};
              cout << join(v, ", ") << endl;
              string s = join(v, '+').str();
          }
          

          代码是:

          template<typename Iterable, typename Sep>
          class Joiner {
              const Iterable& i_;
              const Sep& s_;
          public:
              Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {}
              std::string str() const {std::stringstream ss; ss << *this; return ss.str();}
              template<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j);
          };
          
          template<typename I, typename S>
          std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j) {
              auto elem = j.i_.begin();
              if (elem != j.i_.end()) {
                  os << *elem;
                  ++elem;
                  while (elem != j.i_.end()) {
                      os << j.s_ << *elem;
                      ++elem;
                  }
              }
              return os;
          }
          
          template<typename I, typename S>
          inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}
          

          【讨论】:

            【解决方案14】:

            以下是将vector中的元素转换为string的简单实用的方法:

            std::string join(const std::vector<int>& numbers, const std::string& delimiter = ",") {
                std::ostringstream result;
                for (const auto number : numbers) {
                    if (result.tellp() > 0) { // not first round
                        result << delimiter;
                    }
                    result << number;
                }
                return result.str();
            }
            

            您需要#include &lt;sstream&gt;ostringstream

            【讨论】:

              【解决方案15】:

              扩展@sbi 在generic solution 上的尝试,不限于std::vector&lt;int&gt; 或特定的返回字符串类型。下面给出的代码可以这样使用:

              std::vector<int> vec{ 1, 2, 3 };
              
              // Call modern range-based overload.
              auto str     = join( vec,  "," );
              auto wideStr = join( vec, L"," );
              
              // Call old-school iterator-based overload.
              auto str     = join( vec.begin(), vec.end(),  "," );
              auto wideStr = join( vec.begin(), vec.end(), L"," );
              

              在原始代码中,如果分隔符是字符串文字(如上面的示例中所示),则模板参数推导无法生成正确的返回字符串类型。在这种情况下,函数体中的 Str::value_type 这样的 typedef 是不正确的。该代码假定Str 始终是std::basic_string 之类的类型,因此对于字符串文字显然失败。

              为了解决这个问题,以下代码尝试仅从分隔符参数中推断出 character 类型,并使用它来生成默认返回字符串类型。这是使用boost::range_value 实现的,它从给定的range 类型中提取元素类型。

              #include <string>
              #include <sstream>
              #include <boost/range.hpp>
              
              template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt >
              Str join( InputIt first, const InputIt last, const Sep& sep )
              {
                  using char_type          = typename Str::value_type;
                  using traits_type        = typename Str::traits_type;
                  using allocator_type     = typename Str::allocator_type;
                  using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >;
              
                  ostringstream_type result;
              
                  if( first != last )
                  {
                      result << *first++;
                  }
                  while( first != last ) 
                  {
                      result << sep << *first++;
                  }
                  return result.str();
              }
              

              现在我们可以轻松地提供一个基于范围的重载,它简单地转发到基于迭代器的重载:

              template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange>
              Str join( const InputRange &input, const Sep &sep )
              {
                  // Include the standard begin() and end() in the overload set for ADL. This makes the 
                  // function work for standard types (including arrays), aswell as any custom types 
                  // that have begin() and end() member functions or overloads of the standalone functions.
                  using std::begin; using std::end;
              
                  // Call iterator-based overload.
                  return join( begin(input), end(input), sep );
              }
              

              Live Demo at Coliru

              【讨论】:

                【解决方案16】:

                为什么这里的答案如此复杂

                string vec2str( vector<int> v){
                        string s="";
                        for (auto e: v){
                            s+=to_string(e);
                            s+=',';
                        }
                        s.pop_back();
                        return s;
                   }
                

                【讨论】:

                  【解决方案17】:

                  正如@capone 所做的那样,

                  std::string join(const std::vector<std::string> &str_list , 
                                   const std::string &delim=" ")
                  {
                      if(str_list.size() == 0) return "" ;
                      return std::accumulate( str_list.cbegin() + 1, 
                                              str_list.cend(), 
                                              str_list.at(0) , 
                                              [&delim](const std::string &a , const std::string &b)
                                              { 
                                                  return a + delim + b ;
                                              }  ) ; 
                  }
                  
                  template <typename ST , typename TT>
                  std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec)
                  {
                      vector<TT> rst ;
                      std::transform(ori_vec.cbegin() ,
                                    ori_vec.cend() , back_inserter(rst) , 
                                    [&op](const ST& val){ return op(val)  ;} ) ;
                      return rst ;
                  }
                  

                  然后我们可以像下面这样调用:

                  int main(int argc , char *argv[])
                  {
                      vector<int> int_vec = {1,2,3,4} ;
                      vector<string> str_vec = map<int,string>(to_string, int_vec) ;
                      cout << join(str_vec) << endl ;
                      return 0 ;
                  }
                  

                  就像蟒蛇:

                  >>> " ".join( map(str, [1,2,3,4]) )
                  

                  【讨论】:

                    【解决方案18】:

                    我用这样的东西

                    namespace std
                    {
                    
                    // for strings join
                    string to_string( string value )
                    {
                        return value;
                    }
                    
                    } // namespace std
                    
                    namespace // anonymous
                    {
                    
                    template< typename T >
                    std::string join( const std::vector<T>& values, char delimiter )
                    {
                        std::string result;
                        for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx )
                        {
                            if( idx != 0 )
                                result += delimiter;
                            result += std::to_string( values[idx] );
                        }
                        return result;
                    }
                    
                    } // namespace anonymous
                    

                    【讨论】:

                      【解决方案19】:

                      我从@sbi 的回答开始,但大多数时候最终将生成的字符串通过管道传输到流中,因此创建了以下解决方案,该解决方案可以通过管道传输到流,而无需在内存中创建完整的字符串。

                      用法如下:

                      #include "string_join.h"
                      #include <iostream>
                      #include <vector>
                      
                      int main()
                      {
                        std::vector<int> v = { 1, 2, 3, 4 };
                        // String version
                        std::string str = join(v, std::string(", "));
                        std::cout << str << std::endl;
                        // Directly piped to stream version
                        std::cout << join(v, std::string(", ")) << std::endl;
                      }
                      

                      string_join.h 在哪里:

                      #pragma once
                      
                      #include <iterator>
                      #include <sstream>
                      
                      template<typename Str, typename It>
                      class joined_strings
                      {
                        private:
                          const It begin, end;
                          Str sep;
                      
                        public:
                          typedef typename Str::value_type char_type;
                          typedef typename Str::traits_type traits_type;
                          typedef typename Str::allocator_type allocator_type;
                      
                        private:
                          typedef std::basic_ostringstream<char_type, traits_type, allocator_type>
                            ostringstream_type;
                      
                        public:
                          joined_strings(It begin, const It end, const Str &sep)
                            : begin(begin), end(end), sep(sep)
                          {
                          }
                      
                          operator Str() const
                          {
                            ostringstream_type result;
                            result << *this;
                            return result.str();
                          }
                      
                          template<typename ostream_type>
                          friend ostream_type& operator<<(
                            ostream_type &ostr, const joined_strings<Str, It> &joined)
                          {
                            It it = joined.begin;
                            if(it!=joined.end)
                              ostr << *it;
                            for(++it; it!=joined.end; ++it)
                              ostr << joined.sep << *it;
                            return ostr;
                          }
                      };
                      
                      template<typename Str, typename It>
                      inline joined_strings<Str, It> join(It begin, const It end, const Str &sep)
                      {
                        return joined_strings<Str, It>(begin, end, sep);
                      }
                      
                      template<typename Str, typename Container>
                      inline joined_strings<Str, typename Container::const_iterator> join(
                        Container container, const Str &sep)
                      {
                        return join(container.cbegin(), container.cend(), sep);
                      }
                      

                      【讨论】:

                        【解决方案20】:

                        我编写了以下代码。它基于 C# string.join。它适用于 std::string 和 std::wstring 以及许多类型的向量。 (cmets 中的示例)

                        这样称呼它:

                         std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};
                        
                         std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');
                        

                        代码:

                        // Generic Join template (mimics string.Join() from C#)
                        // Written by RandomGuy (stackoverflow) 09-01-2017
                        // Based on Brian R. Bondy anwser here:
                        // http://stackoverflow.com/questions/1430757/c-vector-to-string
                        // Works with char, wchar_t, std::string and std::wstring delimiters
                        // Also works with a different types of vectors like ints, floats, longs
                        template<typename T, typename D>
                        auto Join(const std::vector<T> &vToMerge, const D &delimiter)
                        {
                            // We use std::conditional to get the correct type for the stringstream (char or wchar_t)
                            // stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t>
                            using strType =
                                std::conditional<
                                std::is_same<D, std::string>::value,
                                char,
                                    std::conditional<
                                    std::is_same<D, char>::value,
                                    char,
                                    wchar_t
                                    >::type
                                >::type;
                        
                            std::basic_stringstream<strType> ss;
                        
                            for (size_t i = 0; i < vToMerge.size(); ++i)
                            {
                                if (i != 0)
                                    ss << delimiter;
                                ss << vToMerge[i];
                            }
                            return ss.str();
                        }
                        

                        【讨论】:

                          【解决方案21】:

                          这是一种将整数向量转换为字符串的简单方法。

                          #include <bits/stdc++.h>
                          using namespace std;
                          int main()
                          {
                              vector<int> A = {1, 2, 3, 4};
                              string s = "";
                              for (int i = 0; i < A.size(); i++)
                              {
                                  s = s + to_string(A[i]) + ",";
                              }
                              s = s.substr(0, s.length() - 1); //Remove last character
                              cout << s;
                          }
                          

                          【讨论】:

                            【解决方案22】:

                            使用模板函数加入

                            我使用templatefunction 加入vector 项目,并通过仅迭代vector 中的第一个到倒数第二个项目,然后加入最后一个项目,删除了不必要的if 语句for 循环。这也避免了需要额外的代码来删除连接字符串末尾的额外分隔符。因此,没有if 语句会减慢迭代速度,也没有需要整理的多余分隔符。

                            这会产生一个优雅的函数调用来加入stringintegerdoublevector

                            我写了两个版本:一个返回一个字符串;另一个返回一个字符串。另一个直接写入流。

                            #include <iostream>
                            #include <sstream>
                            #include <string>
                            #include <vector>
                            using namespace std;
                            
                            // Return a string of joined vector items.
                            template<typename T>
                            string join(const vector<T>& v, const string& sep)
                            {
                                ostringstream oss;
                                const auto LAST = v.end() - 1;
                                // Iterate through the first to penultimate items appending the separator.
                                for (typename vector<T>::const_iterator p = v.begin(); p != LAST; ++p)
                                {
                                    oss << *p << sep;
                                }
                                // Join the last item without a separator.
                                oss << *LAST;
                                return oss.str();
                            }
                            
                            // Write joined vector items directly to a stream.
                            template<typename T>
                            void join(const vector<T>& v, const string& sep, ostream& os)
                            {
                                const auto LAST = v.end() - 1;
                                // Iterate through the first to penultimate items appending the separator.
                                for (typename vector<T>::const_iterator p = v.begin(); p != LAST; ++p)
                                {
                                    os << *p << sep;
                                }
                                // Join the last item without a separator.
                                os << *LAST;
                            }
                            
                            int main()
                            {
                                vector<string> strings
                                {
                                    "Joined",
                                    "from",
                                    "beginning",
                                    "to",
                                    "end"
                                };
                                vector<int> integers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
                                vector<double> doubles{ 1.2, 3.4, 5.6, 7.8, 9.0 };
                            
                                cout << join(strings, "... ") << endl << endl;
                                cout << join(integers, ", ") << endl << endl;
                                cout << join(doubles, "; ") << endl << endl;
                            
                                join(strings, "... ", cout);
                                cout << endl << endl;
                                join(integers, ",  ", cout);
                                cout << endl << endl;
                                join(doubles, ";  ", cout);
                                cout << endl << endl;
                            
                                return 0;
                            }
                            

                            输出

                            Joined... from... beginning... to... end
                            
                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                            
                            1.2; 3.4; 5.6; 7.8; 9
                            
                            Joined... from... beginning... to... end
                            
                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                            
                            1.2; 3.4; 5.6; 7.8; 9
                            

                            【讨论】:

                              【解决方案23】:
                              #include <iostream>
                              #include <vector>
                              
                              int main()
                              {
                                  std::vector<int> v{{1,2,3,4}};
                                  std::string str;
                              
                                  // ----->
                                  if (! v.empty())
                                  {
                                      str = std::to_string(*v.begin());
                                      for (auto it = std::next(v.begin()); it != v.end(); ++it)
                                          str.append("," + std::to_string(*it));
                                  }
                                  // <-----
                                  
                                  std::cout << str << "\n";
                              }
                              

                              【讨论】:

                                【解决方案24】:

                                使用数字库 (#include &lt;numeric&gt;) 中的 std::accumulate 解决此问题的另一种方法:

                                std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
                                auto comma_fold = [](std::string a, int b) { return std::move(a) + ',' + std::to_string(b); };
                                std::string s = std::accumulate(std::next(v.begin()), v.end(),
                                                                std::to_string(v[0]), // start with first element
                                                                comma_fold);
                                std::cout << s << std::endl; // 1,2,3,4,5,6,7,8,9,10
                                

                                【讨论】:

                                  猜你喜欢
                                  • 2012-01-24
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2016-11-11
                                  • 1970-01-01
                                  • 2011-10-02
                                  相关资源
                                  最近更新 更多