Làm thế nào để mã hóa một vectơ chuỗi thành một chuỗi (một cách dễ hiểu)


81

Tôi đang tìm cách thanh lịch nhất để mã hóa một vector chuỗi thành một chuỗi. Dưới đây là giải pháp hiện tôi đang sử dụng:

static std::string& implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
    for (std::vector<std::string>::const_iterator ii = elems.begin(); ii != elems.end(); ++ii)
    {
        s += (*ii);
        if ( ii + 1 != elems.end() ) {
            s += delim;
        }
    }

    return s;
}

static std::string implode(const std::vector<std::string>& elems, char delim)
{
    std::string s;
    return implode(elems, delim, s);
}

Có ai khác ngoài đó không?


Tại sao bạn gọi chức năng này là implode?
Colonel Panic

5
@ColonelPanic, tương tự với phương thức implode () của PHP, kết hợp các phần tử mảng và xuất chúng dưới dạng một chuỗi đơn. Tôi tự hỏi tại sao các người hỏi câu hỏi này :)
ezpresso

Câu trả lời:


133

Sử dụng boost::algorithm::join(..):

#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);

Xem thêm câu hỏi này .


54
Đề xuất bao gồm và liên kết chống lại thư viện tăng khổng lồ để tạo một chuỗi đơn giản là vô lý.
Julian

8
@Julian hầu hết các dự án đã làm điều này. Tuy nhiên, tôi đồng ý rằng thật vô lý khi STL không bao gồm cách để thực hiện việc này. Tôi cũng có thể đồng ý rằng đây không phải là câu trả lời hàng đầu , nhưng các câu trả lời khác rõ ràng có sẵn.
River Tam

Tôi đồng tình với @Julian. Boost có thể thanh lịch khi sử dụng nhưng không phải là "cách thanh lịch nhất" về chi phí. Trong trường hợp này, đó là một cách giải quyết cho thuật toán của OP chứ không phải là một giải pháp cho chính câu hỏi.
Azeroth2b

3
Hầu hết các thư viện Boost chỉ ở dạng tiêu đề, vì vậy không có gì để liên kết. Một số thậm chí còn đi vào tiêu chuẩn.
jbruni

27
std::vector<std::string> strings;

const char* const delim = ", ";

std::ostringstream imploded;
std::copy(strings.begin(), strings.end(),
           std::ostream_iterator<std::string>(imploded, delim));

(bao gồm <string>, <vector>, <sstream><iterator>)

Nếu bạn muốn có một kết thúc rõ ràng (không có dấu phân cách ở cuối), hãy xem ở đây


9
Tuy nhiên, hãy nhớ rằng nó sẽ thêm dấu phân cách bổ sung (tham số thứ hai cho hàm std::ostream_iteratortạo ở cuối luồng.
Michael Krelin - hacker.

9
Điểm của "implode" là không nên thêm dấu phân cách vào cuối cùng. Câu trả lời này không may là thêm dấu phân cách cuối cùng.
Jonny

20

Bạn nên sử dụng std::ostringstreamthay vì std::stringxây dựng đầu ra (sau đó bạn có thể gọi str()phương thức của nó ở cuối để lấy một chuỗi, vì vậy giao diện của bạn không cần thay đổi, chỉ là tạm thời s).

Từ đó, bạn có thể chuyển sang sử dụng std::ostream_iterator, như sau:

copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim)); 

Nhưng điều này có hai vấn đề:

  1. delimbây giờ cần phải là một const char*, hơn là một đơn lẻ char. Không phải vấn đề lớn.
  2. std::ostream_iteratorviết dấu phân cách sau mỗi phần tử, kể cả phần tử cuối cùng. Vì vậy, bạn cần phải xóa cái cuối cùng ở cuối hoặc viết phiên bản trình lặp của riêng bạn mà không gây khó chịu này. Sẽ rất đáng làm phần sau nếu bạn có nhiều mã cần những thứ như thế này; nếu không thì tốt nhất có thể tránh toàn bộ sự lộn xộn (tức là sử dụng ostringstreamnhưng không ostream_iterator).

1
Hoặc sử dụng một cái đã được viết sẵn: stackoverflow.com/questions/3496982/…
Jerry Coffin,

13

Bởi vì tôi thích một lớp lót (chúng rất hữu ích cho tất cả những thứ kỳ lạ, như bạn sẽ thấy ở phần cuối), đây là một giải pháp sử dụng std :: Tích lũy và C ++ 11 lambda:

std::accumulate(alist.begin(), alist.end(), std::string(), 
    [](const std::string& a, const std::string& b) -> std::string { 
        return a + (a.length() > 0 ? "," : "") + b; 
    } )

Tôi thấy cú pháp này hữu ích với toán tử luồng, nơi tôi không muốn có tất cả các loại logic kỳ lạ ngoài phạm vi hoạt động luồng, chỉ để thực hiện một phép nối chuỗi đơn giản. Hãy xem xét ví dụ câu lệnh trả về này từ phương thức định dạng một chuỗi bằng cách sử dụng toán tử dòng (sử dụng std;):

return (dynamic_cast<ostringstream&>(ostringstream()
    << "List content: " << endl
    << std::accumulate(alist.begin(), alist.end(), std::string(), 
        [](const std::string& a, const std::string& b) -> std::string { 
            return a + (a.length() > 0 ? "," : "") + b; 
        } ) << endl
    << "Maybe some more stuff" << endl
    )).str();

Cập nhật:

Như đã được chỉ ra bởi @plexando trong các nhận xét, đoạn mã trên bị lỗi khi mảng bắt đầu với chuỗi trống do thực tế là kiểm tra "lần chạy đầu tiên" bị thiếu các lần chạy trước đó dẫn đến không có ký tự bổ sung và - thật kỳ lạ khi chạy kiểm tra "lần chạy đầu tiên" trên tất cả các lần chạy (tức là mã chưa được tối ưu hóa).

Giải pháp cho cả hai vấn đề này rất dễ dàng nếu chúng ta biết thực tế rằng danh sách có ít nhất một phần tử. OTOH, nếu chúng ta biết thực tế rằng danh sách không có ít nhất một phần tử, thì chúng ta có thể rút ngắn thời gian chạy hơn nữa.

Tôi nghĩ rằng mã kết quả không đẹp, vì vậy tôi đang thêm nó ở đây dưới dạng Giải pháp chính xác , nhưng tôi nghĩ rằng cuộc thảo luận ở trên vẫn có merrit:

alist.empty() ? "" : /* leave early if there are no items in the list
  std::accumulate( /* otherwise, accumulate */
    ++alist.begin(), alist.end(), /* the range 2nd to after-last */
    *alist.begin(), /* and start accumulating with the first item */
    [](auto& a, auto& b) { return a + "," + b; });

Ghi chú:

  • Đối với các vùng chứa hỗ trợ truy cập trực tiếp đến phần tử đầu tiên, có lẽ tốt hơn nên sử dụng phần tử đó cho đối số thứ ba, thay vào đó alist[0]đối với vectơ.
  • Theo thảo luận trong phần bình luận và trò chuyện, lambda vẫn thực hiện một số bản sao. Điều này có thể được giảm thiểu bằng cách sử dụng lambda (ít đẹp hơn) này thay thế: [](auto&& a, auto&& b) -> auto& { a += ','; a += b; return a; })(trên GCC 10) cải thiện hiệu suất hơn x10. Cảm ơn @Deduplicator về đề xuất. Tôi vẫn đang cố gắng tìm hiểu xem chuyện gì đang xảy ra ở đây.

4
Không sử dụng accumulatecho chuỗi. Hầu hết các câu trả lời khác là O (n) nhưng accumulatelà O (n ^ 2) vì nó tạo một bản sao tạm thời của bộ tích lũy trước khi thêm mỗi phần tử. Và không, ngữ nghĩa di chuyển không giúp ích gì.
Oktalist

2
@Oktalist, tôi không chắc tại sao bạn lại nói như vậy - cplusplus.com/reference/numeric/accumulate nói rằng "Độ phức tạp là tuyến tính trong khoảng cách giữa đầu tiên và cuối cùng".
Guss

1
Điều đó giả định rằng mỗi phép cộng riêng lẻ cần thời gian không đổi. Nếu Tcó quá tải operator+(giống như stringkhông) hoặc nếu bạn cung cấp bộ điều khiển của riêng mình thì tất cả các cược sẽ bị tắt. Mặc dù tôi có thể đã vội vàng khi nói rằng ngữ nghĩa chuyển động không giúp ích gì, nhưng chúng không giải quyết được vấn đề trong hai cách triển khai mà tôi đã kiểm tra. Xem câu trả lời của tôi cho những câu hỏi tương tự .
Oktalist

1
bình luận của skwllsp không có gì để làm với nó. Như tôi đã nói, hầu hết các câu trả lời khác (và implodeví dụ của OP ) đang làm đúng. Chúng là O (n), ngay cả khi chúng không gọi reservetrên chuỗi. Chỉ có giải pháp sử dụng tích lũy là O (n ^ 2). Không cần mã kiểu C.
Oktalist

12
Tôi đã làm một điểm chuẩn và tích lũy thực sự nhanh hơn một luồng O (n) chuỗi.
kirbyfan64sos

10

những gì về giải pháp đơn giản ngu ngốc?

std::string String::join(const std::vector<std::string> &lst, const std::string &delim)
{
    std::string ret;
    for(const auto &s : lst) {
        if(!ret.empty())
            ret += delim;
        ret += s;
    }
    return ret;
}

8
string join(const vector<string>& vec, const char* delim)
{
    stringstream res;
    copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
    return res.str();
}

6

Đặc biệt là với các bộ sưu tập lớn hơn, bạn muốn tránh phải kiểm tra xem bạn có còn thêm phần tử đầu tiên hay không để đảm bảo không có dấu phân tách ở cuối ...

Vì vậy, đối với danh sách rỗng hoặc một phần tử, không có sự lặp lại nào cả.

Phạm vi trống là tầm thường: return "".

Một phần tử hoặc nhiều phần tử có thể được xử lý hoàn hảo bằng cách accumulate:

auto join = [](const auto &&range, const auto separator) {
    if (range.empty()) return std::string();

    return std::accumulate(
         next(begin(range)), // there is at least 1 element, so OK.
         end(range),

         range[0], // the initial value

         [&separator](auto result, const auto &value) {
             return result + separator + value;
         });
};

Mẫu đang chạy ( yêu cầu C ++ 14 ): http://cpp.sh/8uspd


6

Tôi thích sử dụng tích lũy một chữ cái này (không có dấu phân cách ở cuối):

std::accumulate(
    std::next(elems.begin()), 
    elems.end(), 
    elems[0], 
    [](std::string a, std::string b) {
        return a + delimiter + b;
    }
);

1
Hãy cẩn thận khi nó trống rỗng.
Carlos Pinzón

3

Một phiên bản sử dụng std::accumulate:

#include <numeric>
#include <iostream>
#include <string>

struct infix {
  std::string sep;
  infix(const std::string& sep) : sep(sep) {}
  std::string operator()(const std::string& lhs, const std::string& rhs) {
    std::string rz(lhs);
    if(!lhs.empty() && !rhs.empty())
      rz += sep;
    rz += rhs;
    return rz;
  }
};

int main() {
  std::string a[] = { "Hello", "World", "is", "a", "program" };
  std::string sum = std::accumulate(a, a+5, std::string(), infix(", "));
  std::cout << sum << "\n";
}

2

Đây là một cái khác không thêm dấu phân cách sau phần tử cuối cùng:

std::string concat_strings(const std::vector<std::string> &elements,
                           const std::string &separator)
{       
    if (!elements.empty())
    {
        std::stringstream ss;
        auto it = elements.cbegin();
        while (true)
        {
            ss << *it++;
            if (it != elements.cend())
                ss << separator;
            else
                return ss.str();
        }       
    }
    return "";

2

Việc sử dụng một phần của câu trả lời này cho một câu hỏi khác cung cấp cho bạn câu trả lời được nối với nhau, dựa trên dấu phân cách không có dấu phẩy ở cuối,

Sử dụng:

std::vector<std::string> input_str = std::vector<std::string>({"a", "b", "c"});
std::string result = string_join(input_str, ",");
printf("%s", result.c_str());
/// a,b,c

Mã:

std::string string_join(const std::vector<std::string>& elements, const char* const separator)
{
    switch (elements.size())
    {
        case 0:
            return "";
        case 1:
            return elements[0];
        default:
            std::ostringstream os;
            std::copy(elements.begin(), elements.end() - 1, std::ostream_iterator<std::string>(os, separator));
            os << *elements.rbegin();
            return os.str();
    }
}

0

Giải pháp hơi dài, nhưng không sử dụng std::ostringstreamvà không yêu cầu hack để xóa dấu phân cách cuối cùng.

http://www.ideone.com/hW1M9

Và mã:

struct appender
{
  appender(char d, std::string& sd, int ic) : delim(d), dest(sd), count(ic)
  {
    dest.reserve(2048);
  }

  void operator()(std::string const& copy)
  {
    dest.append(copy);
    if (--count)
      dest.append(1, delim);
  }

  char delim;
  mutable std::string& dest;
  mutable int count;
};

void implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
  std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size()));
}

0

Đây là những gì tôi sử dụng, đơn giản và linh hoạt

string joinList(vector<string> arr, string delimiter)
{
    if (arr.empty()) return "";

    string str;
    for (auto i : arr)
        str += i + delimiter;
    str = str.substr(0, str.size() - delimiter.size());
    return str;
}

sử dụng:

string a = joinList({ "a", "bbb", "c" }, "!@#");

đầu ra:

a!@#bbb!@#c

-1

chỉ cần thêm !! Chuỗi s = "";

for (int i = 0; i < doc.size(); i++)   //doc is the vector
    s += doc[i];

-1

hãy thử điều này, nhưng sử dụng vector thay vì danh sách

template <class T>
std::string listToString(std::list<T> l){
    std::stringstream ss;
    for(std::list<int>::iterator it = l.begin(); it!=l.end(); ++it){
        ss << *it;
        if(std::distance(it,l.end())>1)
            ss << ", ";
    }
    return "[" + ss.str()+ "]";
}
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.