Chuyển đổi một vectơ <int> thành một chuỗi


92

Tôi có một vùng vector<int>chứa chứa các số nguyên (ví dụ: {1,2,3,4}) và tôi muốn chuyển đổi thành một chuỗi có dạng

"1,2,3,4"

Cách sạch nhất để làm điều đó trong C ++ là gì? Trong Python, đây là cách tôi thực hiện:

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

Câu trả lời:


95

Chắc chắn là không thanh lịch như Python, nhưng không có gì thanh lịch như Python trong C ++.

Bạn có thể sử dụng 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();

Bạn cũng có thể sử dụng std::for_eachthay thế.


Tôi nghĩ bạn có nghĩa là array.size () không phải v.size (), không?
Mark Elliot vào

1
ya bất kỳ vector nào được gọi.
Brian R. Bondy,

2
Điều đó nên được std::string s = ss.str(). Nếu bạn muốn const char*, hãy sử dụng s.c_str(). (Lưu ý rằng, trong khi đúng cú pháp, ss.str().c_str()sẽ cung cấp cho bạn một const char*trỏ đến một tạm thời, sẽ sẽ không còn tồn tại ở phần cuối của sự biểu hiện đầy đủ Đó đau..)
SBI

1
tại sao không chỉ sử dụng string.append?
Baiyan Huang

12
câu trả lời là không đầy đủ nếu không có#include <sstream>
renadeen

43

Sử dụng std :: for_each và lambda, bạn có thể làm điều gì đó thú vị.

#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=',';
                   });
}

Xem câu hỏi này cho một lớp học nhỏ tôi đã viết. Điều này sẽ không in dấu phẩy ở cuối. Ngoài ra, nếu chúng ta giả định rằng C ++ 14 sẽ tiếp tục cung cấp cho chúng ta các thuật toán tương đương dựa trên phạm vi như thế này:

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
}

12
Tôi nghĩ điều này không hoàn toàn tương đương với phép nối của Python - nó sẽ chèn thêm dấu "," ở cuối.
THÔNG TIN 1800

2
Không tương đương nhưng chỉ là thanh lịch (thực tế tôi nghĩ nhiều hơn như vậy nhưng đó chỉ là một ý kiến).
Martin York

20
Rõ ràng sự thanh lịch là chủ quan. Vì vậy, nếu bạn và hai người khác thích còn, mã lặp đi lặp lại nhiều hơn mà không làm việc, sau đó nó ;-p thêm thanh lịch
Steve Jessop

1
Bạn có thể bỏ qua dấu phẩy cuối cùng bằng cách sử dụng hàm thành viên string :: substr. Gán chuỗi con từ 0 đến n-1 cho biến kết quả của bạn.
Dan

@SteveJessop: Tốt hơn?
Martin York

24

Bạn có thể sử dụng std :: tích lũy. Hãy xem xét ví dụ sau

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);
                     });

','nên được","
Matt

2
@Matt stringLớp có một +toán tử quá tải cũng có thể chấp nhận các ký tự. Vì vậy, ','là tốt.
Pavan Manjunath

19

Một thay thế khác là sử dụng std::copyostream_iteratorlớp:

#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);

Cũng không đẹp bằng Python. Với mục đích này, tôi đã tạo một joinhàm:

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;
}

Sau đó, sử dụng nó như thế này:

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

Bạn có thể hỏi tại sao tôi đã vượt qua trong trình lặp. Thực ra tôi muốn đảo ngược mảng, vì vậy tôi đã sử dụng nó như thế này:

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

Lý tưởng nhất, tôi muốn tạo mẫu đến mức nó có thể suy ra kiểu char và sử dụng chuỗi-luồng, nhưng tôi chưa thể tìm ra điều đó.


Vì nó sẽ là quá nhiều cho một nhận xét, tôi đã đăng một câu trả lời ( stackoverflow.com/questions/1430757/1432040#1432040 ) để cố gắng giải câu đố được đưa ra trong câu cuối cùng của bạn.
sbi 16/09/09

joinHàm của bạn cũng có thể được sử dụng với vectơ? Xin bạn vui lòng cho ví dụ, tôi mới làm quen với C ++.
Noitidart

Bạn có thể thay đổi biến lặp thành preincrement trong câu trả lời không?
Millie Smith

14

Với Boost và C ++ 11, điều này có thể đạt được như sau:

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

Chà, gần như vậy. Đây là ví dụ đầy đủ:

#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;
}

Tín dụng để thuộc về pháp quan .

Bạn có thể xử lý bất kỳ loại giá trị nào như sau:

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);
};

11

Đây chỉ là một nỗ lực để giải câu đố được đưa ra bởi nhận xét của 1800 INFORMATION về giải pháp thứ hai của ông thiếu tính tổng quát, không phải là một nỗ lực để trả lời câu hỏi:

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();
}

Hoạt động trên máy của tôi (TM).


Visual Studio 2013 rất khó hiểu bởi các typedefs. Không phải bạn có thể biết điều đó vào năm 2009.
Grault

@Jes: Tôi đã nhìn chằm chằm vào điều này trong 5 phút, nhưng không thể tìm ra những gì VS có thể vượt qua. Bạn có thể cụ thể hơn không?
sbi

Tôi xin lỗi, tôi nghĩ rằng tôi đã cố gắng kết hợp các đối tượng mà không có << quá tải, mà bây giờ tôi nhận ra là không phù hợp với mã của bạn. Tôi không thể khiến mã của bạn không được biên dịch với một vectơ chuỗi. Lưu ý nhỏ, VS 2013 Community vừa miễn phí vừa có nhiều tính năng, không giống như phiên bản "Express".
Grault

@Jes: Điều này sẽ hoạt động với bất kỳ loại nào có thể được phát trực tuyến (tức là đã operator<<quá tải). Tất nhiên, một loại không operator<<có có thể gây ra các thông báo lỗi rất khó hiểu.
sbi

Thật không may, điều này không biên dịch: join(v.begin(), v.end(), ","). Suy luận đối số mẫu không tạo ra kết quả đúng nếu sepđối số là một chuỗi ký tự. Cố gắng của tôi về một giải pháp cho vấn đề này . Đồng thời cung cấp quá tải dựa trên phạm vi hiện đại hơn.
zett42 26/09/18

7

Rất nhiều mẫu / ý tưởng. Của tôi không phải là chung chung hay hiệu quả, nhưng tôi chỉ có cùng một vấn đề và muốn đưa nó vào hỗn hợp như một cái gì đó ngắn gọn và ngọt ngào. Nó thắng trên số dòng ngắn nhất ... :)

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);

1
Cảm ơn vì mã đơn giản. Có thể muốn thay đổi nó thành "tự động &" để tránh các bản sao thừa và nhận được một số hiệu suất dễ dàng.
Millie Smith

Thay vì sử dụng substr(...), hãy sử dụng pop_back()để loại bỏ ký tự cuối cùng, trở nên rõ ràng và sạch sẽ hơn nhiều.
ifyalciner

4

Nếu muốn std::cout << join(myVector, ",") << std::endl;, bạn có thể làm như sau:

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));
}

Lưu ý, giải pháp này thực hiện tham gia trực tiếp vào luồng đầu ra chứ không phải tạo bộ đệm phụ và sẽ hoạt động với bất kỳ kiểu nào có toán tử << trên một ostream.

Điều này cũng hoạt động nếu boost::algorithm::join()không thành công, khi bạn có một vector<char*>thay vì a vector<string>.


4
string s;
for (auto i : v)
    s += (s.empty() ? "" : ",") + to_string(i);

7
Chào mừng bạn đến với Stack Overflow! Mặc dù mã này có thể giải quyết được vấn đề, nhưng tốt nhất là bạn nên bổ sung thêm chi tiết và giải thích cách hoạt động của nó cho những người có thể không hiểu đoạn mã này.
paper1111,

1
Câu trả lời hàng đầu hiện tại không phức tạp hơn nhiều và đây là câu trả lời nhỏ nhất / sạch nhất. Không hiệu quả như std::stringstreamđối với mảng lớn vì stringstreamsẽ có thể phân bổ bộ nhớ một cách lạc quan, dẫn đến hiệu suất O (n.log (n)) thay vì O (n²) cho một mảng có kích thước ncho câu trả lời này. Cũng stringstreamcó thể không tạo chuỗi tạm thời cho to_string(i).
aberaud 17/02/19

2

Tôi thích câu trả lời của 1800. Tuy nhiên, tôi sẽ di chuyển lần lặp đầu tiên ra khỏi vòng lặp vì kết quả của câu lệnh if chỉ thay đổi một lần sau lần lặp đầu tiên

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;
}

Tất nhiên điều này có thể được giảm xuống ít câu lệnh hơn nếu bạn muốn:

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;
}

Bạn không nên sử dụng tăng sau cho các loại trình lặp không xác định. Điều đó có thể tốn kém. (Tất nhiên, khi giao dịch với chuỗi, có thể không làm cho rằng có rất nhiều sự khác biệt Nhưng một khi bạn tìm hiểu những thói quen ....)
SBI

Tăng bài viết là được miễn là bạn sử dụng giá trị tạm thời được trả về. ví dụ: "result.append (* it); ++ it;" hầu như luôn đắt như "result.append (* it ++);" thứ hai có thêm một bản sao của trình lặp.
iain 17/09/09

Rất tiếc, tôi vừa phát hiện thấy sự gia tăng bài đăng trong vòng lặp for. sao chép và dán lỗi. Tôi đã sửa bài.
iain 17/09/09

1
@Ian: Khi tôi dạy C ++, tôi đã yêu cầu sinh viên của mình sử dụng ++itrừ những nơi họ thực sự cần i++vì đó là cách duy nhất để họ không quên điều này khi nó tạo ra sự khác biệt. (Tôi cũng vậy, BTW.) Họ đã học Java trước đây, nơi mà tất cả các loại C-isms đang thịnh hành và họ phải mất vài tháng (1 bài giảng + bài làm trong phòng thí nghiệm mỗi tuần), nhưng cuối cùng hầu hết họ đã học được thói quen sử dụng gia số trước.
sbi 17/09/09

1
@sbi: đã đồng ý, tôi cũng luôn mặc định là preincrement, vị trí giả mạo đến từ việc sao chép ai đó elses for loop và thay đổi nó. Trong câu trả lời đầu tiên của tôi, tôi nghĩ rằng bạn đang lo lắng về "result.append (* it ++)" chứ không phải vòng lặp for. Tôi đã hơi xấu hổ khi thấy sự gia tăng bài đăng trong vòng lặp. Một số người dường như làm theo lời khuyên là không sử dụng tăng dần bài đăng quá xa và không bao giờ sử dụng nó hoặc thay đổi nó ngay cả khi nó phù hợp. Tuy nhiên tôi biết bây giờ bạn không thuộc loại này.
iain 18/09/09

2

Có một số nỗ lực thú vị trong việc cung cấp một giải pháp tốt cho vấn đề. Tôi đã có ý tưởng sử dụng các luồng tạo khuôn mẫu để giải đáp hiệu quả tình huống khó xử ban đầu của OP. Mặc dù đây là một bài viết cũ, tôi hy vọng những người dùng tương lai tình cờ gặp phải vấn đề này sẽ thấy giải pháp của tôi có lợi.

Đầu tiên, một số câu trả lời (bao gồm cả câu trả lời được chấp nhận) không thúc đẩy khả năng tái sử dụng. Vì C ++ không cung cấp một cách thanh lịch để nối các chuỗi trong thư viện chuẩn (mà tôi đã thấy), điều quan trọng là phải tạo một chuỗi linh hoạt và có thể tái sử dụng. Đây là bức ảnh của tôi về nó:

// 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;
    }
}

Bây giờ để sử dụng điều này, bạn chỉ cần làm như sau:

// 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;

Lưu ý cách sử dụng các luồng làm cho giải pháp này trở nên vô cùng linh hoạt vì chúng ta có thể lưu trữ kết quả của mình trong một chuỗi chuỗi để lấy lại nó sau này hoặc chúng ta có thể ghi trực tiếp ra chuẩn, một tệp hoặc thậm chí vào một kết nối mạng được triển khai dưới dạng một luồng. Loại được in đơn giản phải có thể lặp lại và tương thích với luồng nguồn. STL cung cấp nhiều luồng khác nhau tương thích với nhiều loại khác nhau. Vì vậy, bạn thực sự có thể đi đến thị trấn với cái này. Tôi nghĩ, vectơ của bạn có thể là int, float, double, string, unsigned int, SomeObject *, v.v.


1

Tôi đã tạo tệp tiêu đề trình trợ giúp để thêm hỗ trợ tham gia mở rộng.

Chỉ cần thêm mã bên dưới vào tệp tiêu đề chung của bạn và đưa vào khi cần.

Ví dụ sử dụng:

/* 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;
}

Đoạn mã đằng sau hiện trường:

#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);
}

1

Đây là giải pháp C ++ 11 chung cho phép bạn làm

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

Mã là:

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);}

1

Sau đây là một cách đơn giản và thiết thực để chuyển đổi các phần tử trong a vectorthành a 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();
}

Bạn cần #include <sstream>cho ostringstream.


1

Mở rộng theo nỗ lực của @sbi tại một giải pháp chung không bị giới hạn std::vector<int>hoặc một loại chuỗi trả về cụ thể. Đoạn mã được trình bày dưới đây có thể được sử dụng như sau:

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"," );

Trong mã gốc, việc suy diễn đối số mẫu không hoạt động để tạo ra loại chuỗi trả về đúng nếu dấu phân tách là một chuỗi ký tự (như trong các mẫu ở trên). Trong trường hợp này, các typedef như Str::value_typetrong phần thân hàm không chính xác. Mã giả định rằng Strluôn luôn là một kiểu như std::basic_stringvậy, vì vậy nó hiển nhiên không thành công đối với các ký tự chuỗi.

Để khắc phục điều này, đoạn mã sau chỉ cố gắng suy ra kiểu ký tự từ đối số dấu phân tách và sử dụng kiểu đó để tạo ra kiểu chuỗi trả về mặc định. Điều này đạt được bằng cách boost::range_valuetrích xuất loại phần tử từ loại phạm vi đã cho .

#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();
}

Giờ đây, chúng ta có thể dễ dàng cung cấp quá tải dựa trên phạm vi mà chỉ cần chuyển tiếp đến quá tải dựa trên trình lặp:

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 );
}

Demo trực tiếp tại Coliru


0

như @capone đã làm,

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 ;
}

Sau đó, chúng ta có thể gọi như sau:

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 ;
}

giống như python:

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

0

Tôi sử dụng một cái gì đó như thế này

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

0

Tôi bắt đầu với câu trả lời của @ sbi nhưng hầu hết thời gian kết thúc việc chuyển chuỗi kết quả thành một luồng, vì vậy, đã tạo giải pháp dưới đây có thể được chuyển tới một luồng mà không cần tạo toàn bộ chuỗi trong bộ nhớ.

Nó được sử dụng như sau:

#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;
}

Trong đó string_join.h là:

#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);
}

0

Tôi đã viết đoạn mã sau. Nó dựa trên C # string.join. Nó hoạt động với std :: string và std :: wstring và nhiều loại vectơ. (ví dụ trong bình luận)

Gọi nó như thế này:

 std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};

 std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');

Mã:

// 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();
}

0

Đây là một cách dễ dàng để chuyển đổi một vectơ số nguyên thành chuỗi.

#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;
}

0

tham gia bằng cách sử dụng chức năng mẫu

Tôi đã sử dụng a template functionđể nối các vectormục và loại bỏ ifcâu lệnh không cần thiết bằng cách chỉ lặp qua các mục đầu tiên đến áp chót trong vòng lặp vector, sau đó nối mục cuối cùng sau forvòng lặp. Điều này cũng loại bỏ nhu cầu về mã bổ sung để loại bỏ dấu phân tách thừa ở cuối chuỗi đã nối. Vì vậy, không có ifcâu lệnh nào làm chậm quá trình lặp lại và không có dấu phân tách thừa cần thu dọn.

Điều này tạo ra một chức năng gọi thanh lịch để tham gia vào một vectorsố string, integerhoặc doublevv

Tôi đã viết hai phiên bản: một phiên bản trả về một chuỗi; cái kia ghi trực tiếp vào một luồng.

#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;
}

Đầu ra

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.