Làm thế nào để tôi lặp lại các từ của một chuỗi?


2986

Tôi đang cố gắng lặp lại các từ của một chuỗi.

Chuỗi có thể được coi là bao gồm các từ được phân tách bằng khoảng trắng.

Lưu ý rằng tôi không quan tâm đến các hàm chuỗi C hoặc loại thao tác / truy cập ký tự đó. Ngoài ra, xin hãy ưu tiên cho sự thanh lịch hơn hiệu quả trong câu trả lời của bạn.

Giải pháp tốt nhất tôi có ngay bây giờ là:

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main()
{
    string s = "Somewhere down the road";
    istringstream iss(s);

    do
    {
        string subs;
        iss >> subs;
        cout << "Substring: " << subs << endl;
    } while (iss);
}

Có một cách thanh lịch hơn để làm điều này?


617
Anh bạn ... Elegance chỉ là một cách thú vị để nói "hiệu quả-trông-đẹp" trong cuốn sách của tôi. Đừng ngại sử dụng các hàm C và các phương thức nhanh để thực hiện bất cứ điều gì chỉ vì nó không có trong một mẫu;)

14
while (iss) { string subs; iss >> subs; cout << "Substring: " << sub << endl; }
pyon

21
@Eduardo: điều đó cũng sai ... bạn cần kiểm tra ngay giữa việc cố gắng truyền phát giá trị khác và sử dụng giá trị đó, tức làstring sub; while (iss >> sub) cout << "Substring: " << sub << '\n';
Tony Delroy

9
Tùy chọn khác nhau trong C ++ để làm điều này theo mặc định: cplusplus.com/faq/sequences/strings/split
hB0

14
Có nhiều thứ sang trọng hơn là hiệu quả khá cao. Thuộc tính thanh lịch bao gồm số lượng dòng thấp và mức độ dễ đọc cao. IMHO Elegance không phải là một proxy cho hiệu quả nhưng khả năng bảo trì.
Matt

Câu trả lời:


1369

Để biết giá trị của nó, đây là một cách khác để trích xuất mã thông báo từ chuỗi đầu vào, chỉ dựa vào các cơ sở thư viện tiêu chuẩn. Đó là một ví dụ về sức mạnh và sự thanh lịch đằng sau thiết kế của STL.

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>

int main() {
    using namespace std;
    string sentence = "And I feel fine...";
    istringstream iss(sentence);
    copy(istream_iterator<string>(iss),
         istream_iterator<string>(),
         ostream_iterator<string>(cout, "\n"));
}

Thay vì sao chép các mã thông báo được trích xuất vào luồng đầu ra, người ta có thể chèn chúng vào một thùng chứa, sử dụng cùng một copythuật toán chung .

vector<string> tokens;
copy(istream_iterator<string>(iss),
     istream_iterator<string>(),
     back_inserter(tokens));

... hoặc tạo vectortrực tiếp:

vector<string> tokens{istream_iterator<string>{iss},
                      istream_iterator<string>{}};

164
Có thể chỉ định một dấu phân cách cho điều này? Ví dụ như chia tách trên dấu phẩy?
l3dx

15
@Jonathan: \ n không phải là dấu phân cách trong trường hợp này, nó là dấu phân cách để xuất ra cout.
huy

772
Đây là một giải pháp kém vì nó không có bất kỳ dấu phân cách nào khác, do đó không thể mở rộng và không thể duy trì.
HelloWorld

37
Trên thực tế, điều này có thể hoạt động tốt với các dấu phân cách khác (mặc dù thực hiện một số điều có phần xấu xí). Bạn tạo một khía cạnh ctype phân loại các dấu phân cách mong muốn thành khoảng trắng, tạo một miền chứa khía cạnh đó, sau đó nhập chuỗi chuỗi với miền địa phương đó trước khi trích xuất chuỗi.
Jerry Coffin

53
@Kinderchocolate "Chuỗi có thể được coi là bao gồm các từ được phân tách bằng khoảng trắng" - Hmm, không có vẻ như là một giải pháp kém cho vấn đề của câu hỏi. "Không thể mở rộng và không thể duy trì" - Hah, một điều tốt đẹp.
Christian Rau

2426

Tôi sử dụng điều này để phân tách chuỗi bởi một dấu phân cách. Cái đầu tiên đặt kết quả vào một vectơ dựng sẵn, cái thứ hai trả về một vectơ mới.

#include <string>
#include <sstream>
#include <vector>
#include <iterator>

template <typename Out>
void split(const std::string &s, char delim, Out result) {
    std::istringstream iss(s);
    std::string item;
    while (std::getline(iss, item, delim)) {
        *result++ = item;
    }
}

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

Lưu ý rằng giải pháp này không bỏ qua các mã thông báo trống, vì vậy, sau đây sẽ tìm thấy 4 mục, một trong số đó là trống:

std::vector<std::string> x = split("one:two::three", ':');

86
Để tránh nó bỏ qua các mã thông báo trống, empty()hãy kiểm tra:if (!item.empty()) elems.push_back(item)
0x499602D2

11
Làm thế nào về delim chứa hai ký tự như ->?
herohuyongtao

7
@herohuyongtao, giải pháp này chỉ hoạt động cho các dấu phân cách char duy nhất.
Evan Teran 27/12/13

4
@JeshwanthKumarNK, không cần thiết, nhưng nó cho phép bạn làm những việc như chuyển kết quả trực tiếp đến một chức năng như thế này: f(split(s, d, v))trong khi vẫn có lợi ích được phân bổ trước vectornếu bạn muốn.
Evan Teran

8
Hãy cẩn thận: split ("one: hai :: ba", ':') và split ("one: Two :: ba:", ':') trả về cùng một giá trị.
dshin

834

Một giải pháp khả thi khi sử dụng Boost có thể là:

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

Cách tiếp cận này có thể còn nhanh hơn stringstreamcách tiếp cận. Và vì đây là một hàm khuôn mẫu chung nên nó có thể được sử dụng để phân chia các loại chuỗi khác (wchar, v.v. hoặc UTF-8) bằng cách sử dụng tất cả các loại dấu phân cách.

Xem tài liệu để biết chi tiết.


35
Tốc độ là không liên quan ở đây, vì cả hai trường hợp này đều chậm hơn nhiều so với chức năng giống strtok.
Tom

45
Và đối với những người chưa tăng ... bcp sao chép hơn 1.000 tệp cho việc này :)
Roman Starkov

12
Cảnh báo, khi được cung cấp một chuỗi rỗng (""), phương thức này trả về một vectơ chứa chuỗi "". Vì vậy, hãy thêm "if (! String_to_split.empty ())" trước khi chia.
Offirmo

29
@Ian Các nhà phát triển nhúng không phải tất cả đều sử dụng boost.
ACK_stoverflow

31
như một phụ lục: Tôi chỉ sử dụng boost khi tôi phải, thông thường tôi thích thêm vào thư viện mã riêng của mình, độc lập và di động để tôi có thể đạt được mã cụ thể chính xác nhỏ, hoàn thành mục tiêu nhất định. Bằng cách đó, mã là không công khai, biểu diễn, tầm thường và di động. Boost có vị trí của nó nhưng tôi sẽ đề nghị rằng một chút quá mức cần thiết cho chuỗi tokenising: bạn sẽ không vận chuyển toàn bộ ngôi nhà của mình đến một công ty kỹ thuật để lấy một cái đinh mới đập vào tường để treo một bức tranh .... họ có thể làm điều đó cực kỳ tốt, nhưng sự khởi sắc vượt xa các nhược điểm.
GMasucci

362
#include <vector>
#include <string>
#include <sstream>

int main()
{
    std::string str("Split me by whitespaces");
    std::string buf;                 // Have a buffer string
    std::stringstream ss(str);       // Insert the string into a stream

    std::vector<std::string> tokens; // Create vector to hold our words

    while (ss >> buf)
        tokens.push_back(buf);

    return 0;
}

12
Bạn cũng có thể phân chia trên các dấu phân cách khác nếu bạn sử dụng getlinetrong whileđiều kiện, ví dụ: để phân tách bằng dấu phẩy, hãy sử dụng while(getline(ss, buff, ',')).
Ali

181

Đối với những người không phù hợp để hy sinh tất cả hiệu quả cho kích thước mã và xem "hiệu quả" là một kiểu thanh lịch, thì điều sau đây sẽ đạt được một điểm ngọt ngào (và tôi nghĩ rằng lớp container mẫu là một bổ sung cực kỳ thanh lịch.):

template < class ContainerT >
void tokenize(const std::string& str, ContainerT& tokens,
              const std::string& delimiters = " ", bool trimEmpty = false)
{
   std::string::size_type pos, lastPos = 0, length = str.length();

   using value_type = typename ContainerT::value_type;
   using size_type  = typename ContainerT::size_type;

   while(lastPos < length + 1)
   {
      pos = str.find_first_of(delimiters, lastPos);
      if(pos == std::string::npos)
      {
         pos = length;
      }

      if(pos != lastPos || !trimEmpty)
         tokens.push_back(value_type(str.data()+lastPos,
               (size_type)pos-lastPos ));

      lastPos = pos + 1;
   }
}

Tôi thường chọn để sử dụng std::vector<std::string>loại như tham số thứ hai của tôi ( ContainerT) ... nhưng list<>là cách nhanh hơn vector<>khi truy cập trực tiếp là không cần thiết, và thậm chí bạn có thể tạo riêng của bạn lớp string và sử dụng một cái gì đó giống như std::list<subString>nơi subStringkhông làm bất kỳ bản sao cho tốc độ đáng kinh ngạc tăng.

Nó nhanh hơn gấp đôi so với mã thông báo nhanh nhất trên trang này và nhanh hơn gần 5 lần so với một số trang khác. Ngoài ra với các loại tham số hoàn hảo, bạn có thể loại bỏ tất cả các chuỗi và liệt kê các bản sao để tăng tốc độ bổ sung.

Ngoài ra, nó không thực hiện kết quả trả về (cực kỳ kém hiệu quả) mà thay vào đó, nó chuyển mã thông báo làm tài liệu tham khảo, do đó cũng cho phép bạn xây dựng mã thông báo bằng nhiều cuộc gọi nếu bạn muốn.

Cuối cùng, nó cho phép bạn chỉ định có nên cắt mã thông báo trống khỏi kết quả thông qua tham số tùy chọn cuối cùng hay không.

Tất cả những gì nó cần là std::string... phần còn lại là tùy chọn. Nó không sử dụng các luồng hoặc thư viện boost, nhưng đủ linh hoạt để có thể chấp nhận một số loại ngoại lai này một cách tự nhiên.


5
Tôi khá hâm mộ điều này, nhưng đối với g ++ (và có lẽ là thực hành tốt), bất kỳ ai sử dụng điều này cũng sẽ muốn typedefs và typenames: typedef ContainerT Base; typedef typename Base::value_type ValueType; typedef typename ValueType::size_type SizeType; Sau đó, để thay thế value_type và size_types tương ứng.
aws

11
Đối với những người trong chúng ta, những người mà công cụ mẫu và nhận xét đầu tiên hoàn toàn xa lạ, một ví dụ sử dụng cmplete với yêu cầu bao gồm sẽ rất đáng yêu.
Wes Miller

3
Ahh tốt, tôi đã tìm ra nó. Tôi đặt các dòng C ++ từ nhận xét của aws bên trong thân hàm của tokenize (), sau đó chỉnh sửa các dòng tokens.push_back () để thay đổi ContainerT :: value_type thành ValueType và thay đổi (ContainerT :: value_type :: size_type) thành ( Kích thước Loại). Đã sửa lỗi các bit g ++ đã rên rỉ về. Chỉ cần gọi nó là tokenize (some_ chuỗi, some_vector);
Wes Miller

2
Ngoài việc chạy một vài thử nghiệm hiệu năng trên dữ liệu mẫu, chủ yếu tôi đã giảm nó xuống càng ít hướng dẫn càng tốt và càng ít bản sao bộ nhớ càng được kích hoạt bằng cách sử dụng lớp chuỗi con chỉ tham chiếu độ lệch / độ dài trong các chuỗi khác. (Tôi tự lăn, nhưng có một số triển khai khác). Thật không may, không có quá nhiều người khác có thể làm để cải thiện điều này, nhưng sự gia tăng gia tăng là có thể.
Marius

3
Đó là đầu ra chính xác cho khi nào trimEmpty = true. Hãy nhớ rằng đó "abo"không phải là một dấu phân cách trong câu trả lời này, mà là danh sách các ký tự phân cách. Thật đơn giản để sửa đổi nó để lấy một chuỗi ký tự phân cách duy nhất (tôi nghĩ str.find_first_ofnên đổi thành str.find_first, nhưng tôi có thể sai ... không thể kiểm tra)
Marius

158

Đây là một giải pháp khác. Nó nhỏ gọn và hiệu quả hợp lý:

std::vector<std::string> split(const std::string &text, char sep) {
  std::vector<std::string> tokens;
  std::size_t start = 0, end = 0;
  while ((end = text.find(sep, start)) != std::string::npos) {
    tokens.push_back(text.substr(start, end - start));
    start = end + 1;
  }
  tokens.push_back(text.substr(start));
  return tokens;
}

Nó có thể dễ dàng được tạo khuôn để xử lý các dấu tách chuỗi, chuỗi rộng, v.v.

Lưu ý rằng việc chia ""kết quả trong một chuỗi trống đơn và tách ","(ví dụ: sep) dẫn đến hai chuỗi trống.

Nó cũng có thể dễ dàng mở rộng để bỏ qua các mã thông báo trống:

std::vector<std::string> split(const std::string &text, char sep) {
    std::vector<std::string> tokens;
    std::size_t start = 0, end = 0;
    while ((end = text.find(sep, start)) != std::string::npos) {
        if (end != start) {
          tokens.push_back(text.substr(start, end - start));
        }
        start = end + 1;
    }
    if (end != start) {
       tokens.push_back(text.substr(start));
    }
    return tokens;
}

Nếu tách một chuỗi tại nhiều dấu phân cách trong khi bỏ qua các mã thông báo trống là mong muốn, phiên bản này có thể được sử dụng:

std::vector<std::string> split(const std::string& text, const std::string& delims)
{
    std::vector<std::string> tokens;
    std::size_t start = text.find_first_not_of(delims), end = 0;

    while((end = text.find_first_of(delims, start)) != std::string::npos)
    {
        tokens.push_back(text.substr(start, end - start));
        start = text.find_first_not_of(delims, end);
    }
    if(start != std::string::npos)
        tokens.push_back(text.substr(start));

    return tokens;
}

10
Phiên bản đầu tiên đơn giản và hoàn thành công việc một cách hoàn hảo. Thay đổi duy nhất tôi sẽ thực hiện là trả về kết quả trực tiếp, thay vì chuyển nó dưới dạng tham số.
gregschlom

2
Đầu ra được thông qua như một tham số cho hiệu quả. Nếu kết quả được trả về, nó sẽ yêu cầu một bản sao của vectơ hoặc phân bổ heap mà sau đó sẽ phải được giải phóng.
Alec Thomas

2
Một phụ lục nhỏ cho nhận xét của tôi ở trên: hàm này có thể trả về vectơ mà không bị phạt nếu sử dụng ngữ nghĩa di chuyển C ++ 11.
Alec Thomas

7
@AlecThomas: Ngay cả trước C ++ 11, hầu hết các trình biên dịch sẽ tối ưu hóa bản sao hoàn trả thông qua NRVO? (+1 dù sao; rất cô đọng)
Marcelo Cantos

11
Trong số tất cả các câu trả lời, đây có vẻ là một trong những câu trả lời hấp dẫn và linh hoạt nhất. Cùng với getline với một dấu phân cách, mặc dù đó là một giải pháp ít rõ ràng hơn. Có phải tiêu chuẩn c ++ 11 không có gì cho việc này? Có c ++ 11 hỗ trợ thẻ đục lỗ những ngày này?
Spacen Jasset

123

Đây là cách yêu thích của tôi để lặp qua một chuỗi. Bạn có thể làm bất cứ điều gì bạn muốn cho mỗi từ.

string line = "a line of text to iterate through";
string word;

istringstream iss(line, istringstream::in);

while( iss >> word )     
{
    // Do something on `word` here...
}

Có thể tuyên bố wordlà một char?
abatishchev

Xin lỗi abatishchev, C ++ không phải là điểm mạnh của tôi. Nhưng tôi tưởng tượng sẽ không khó để thêm một vòng lặp bên trong để lặp qua từng ký tự trong mỗi từ. Nhưng ngay bây giờ tôi tin rằng vòng lặp hiện tại phụ thuộc vào khoảng trắng để phân tách từ. Trừ khi bạn biết rằng chỉ có một nhân vật duy nhất giữa mọi không gian, trong trường hợp này bạn chỉ có thể đúc "chữ" tới char ... xin lỗi tôi không thể được trợ giúp thêm, ive được ý nghĩa để chải lên trên C ++ của tôi
gnomed

11
nếu bạn khai báo từ dưới dạng char, nó sẽ lặp lại trên mọi ký tự không phải khoảng trắng. Nó đủ đơn giản để thử:stringstream ss("Hello World, this is*@#&$(@ a string"); char c; while(ss >> c) cout << c;
Wayne Werner

79

Điều này tương tự với câu hỏi Stack Overflow Làm cách nào để token hóa một chuỗi trong C ++? .

#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>

using namespace std;
using namespace boost;

int main(int argc, char** argv)
{
    string text = "token  test\tstring";

    char_separator<char> sep(" \t");
    tokenizer<char_separator<char>> tokens(text, sep);
    for (const string& t : tokens)
    {
        cout << t << "." << endl;
    }
}

Liệu điều này cụ thể hóa một bản sao của tất cả các mã thông báo, hay nó chỉ giữ vị trí bắt đầu và kết thúc của mã thông báo hiện tại?
einpoklum

66

Tôi thích cách sau vì nó đặt kết quả vào một vectơ, hỗ trợ một chuỗi dưới dạng phân định và cho phép kiểm soát việc giữ các giá trị trống. Nhưng, nó không có vẻ tốt như vậy.

#include <ostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
    vector<string> result;
    if (delim.empty()) {
        result.push_back(s);
        return result;
    }
    string::const_iterator substart = s.begin(), subend;
    while (true) {
        subend = search(substart, s.end(), delim.begin(), delim.end());
        string temp(substart, subend);
        if (keep_empty || !temp.empty()) {
            result.push_back(temp);
        }
        if (subend == s.end()) {
            break;
        }
        substart = subend + delim.size();
    }
    return result;
}

int main() {
    const vector<string> words = split("So close no matter how far", " ");
    copy(words.begin(), words.end(), ostream_iterator<string>(cout, "\n"));
}

Tất nhiên, Boost có một split()phần hoạt động như thế. Và, nếu theo 'khoảng trắng', bạn thực sự có nghĩa là bất kỳ loại khoảng trắng nào, sử dụng phân tách của Boost với is_any_of()các tác phẩm tuyệt vời.


Cuối cùng, một giải pháp xử lý các mã thông báo trống chính xác ở cả hai bên của chuỗi
fmuecke

53

STL không có sẵn một phương pháp như vậy.

Tuy nhiên, bạn có thể sử dụng strtok()chức năng của C bằng cách sử dụng std::string::c_str()thành viên hoặc bạn có thể tự viết. Đây là một mẫu mã tôi tìm thấy sau khi tìm kiếm nhanh trên Google ( "tách chuỗi STL" ):

void Tokenize(const string& str,
              vector<string>& tokens,
              const string& delimiters = " ")
{
    // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos     = str.find_first_of(delimiters, lastPos);

    while (string::npos != pos || string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

Lấy từ: http://oopweb.com/CPP/Document/CPPHOWTO/Volume/C++Programming-HOWTO-7.html

Nếu bạn có câu hỏi về mẫu mã, hãy để lại nhận xét và tôi sẽ giải thích.

Và chỉ vì nó không thực hiện một typedefiterator được gọi hoặc quá tải <<toán tử không có nghĩa đó là mã xấu. Tôi sử dụng chức năng C khá thường xuyên. Ví dụ, printfscanfcả hai đều nhanh hơn std::cinstd::cout(đáng kể), fopencú pháp thân thiện hơn rất nhiều đối với các loại nhị phân và chúng cũng có xu hướng tạo ra các EXE nhỏ hơn.

Đừng để bị bán trong thỏa thuận "Elegance over Performance" này.


Tôi biết về các hàm chuỗi C và tôi cũng biết về các vấn đề về hiệu năng (cả hai điều tôi đã lưu ý trong câu hỏi của mình). Tuy nhiên, đối với câu hỏi cụ thể này, tôi đang tìm kiếm một giải pháp C ++ thanh lịch.
Ashwin Nanjappa

11
@Nelson LaQuet: Hãy để tôi đoán: Bởi vì strtok không được reentrant?
paercebal

40
@Nelson không bao giờ chuyển chuỗi.c_str () cho strtok! strtok bỏ qua chuỗi đầu vào (chèn ký tự '\ 0' để thay thế từng dấu phân cách foudn) và c_str () trả về một chuỗi không thể sửa đổi.
Evan Teran

3
@Nelson: Mảng đó cần phải có kích thước str.size () + 1 trong bình luận cuối cùng của bạn. Nhưng tôi đồng ý với luận điểm của bạn rằng thật ngớ ngẩn khi tránh các chức năng C vì lý do "thẩm mỹ".
j_random_hacker

2
@paulm: Không, sự chậm chạp của các luồng C ++ là do các khía cạnh gây ra. Chúng vẫn chậm hơn các hàm stdio.h ngay cả khi đồng bộ hóa bị tắt (và trên các chuỗi chuỗi, không thể đồng bộ hóa).
Ben Voigt

42

Đây là một chức năng phân chia mà:

  • là chung chung
  • sử dụng C ++ tiêu chuẩn (không tăng)
  • chấp nhận nhiều dấu phân cách
  • bỏ qua các mã thông báo trống (có thể dễ dàng thay đổi)

    template<typename T>
    vector<T> 
    split(const T & str, const T & delimiters) {
        vector<T> v;
        typename T::size_type start = 0;
        auto pos = str.find_first_of(delimiters, start);
        while(pos != T::npos) {
            if(pos != start) // ignore empty tokens
                v.emplace_back(str, start, pos - start);
            start = pos + 1;
            pos = str.find_first_of(delimiters, start);
        }
        if(start < str.length()) // ignore trailing delimiter
            v.emplace_back(str, start, str.length() - start); // add what's left of the string
        return v;
    }

Ví dụ sử dụng:

    vector<string> v = split<string>("Hello, there; World", ";,");
    vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");

Bạn đã quên thêm vào danh sách sử dụng: "cực kỳ kém hiệu quả"
Xander Tulip

1
@XanderTulip, bạn có thể xây dựng hơn và giải thích làm thế nào hoặc tại sao?
Marco M.

3
@XanderTulip: Tôi giả sử bạn đang đề cập đến việc nó trả về vectơ theo giá trị. Tối ưu hóa giá trị trả về (RVO, google nó) nên quan tâm đến vấn đề này. Cũng trong C ++ 11, bạn có thể quay lại bằng cách di chuyển tham chiếu.
Joseph Garvin

3
Điều này thực sự có thể được tối ưu hóa hơn nữa: thay vì .push_back (str.substr (...)) người ta có thể sử dụng .emplace_back (str, start, pos - start). Bằng cách này, đối tượng chuỗi được xây dựng trong vùng chứa và do đó chúng ta tránh được thao tác di chuyển + các shenanigans khác được thực hiện bởi hàm .substr.
Mihai Bişog

@zoopp có. Ý tưởng tốt. VS10 không có hỗ trợ emplace_back khi tôi viết bài này. Tôi sẽ cập nhật câu trả lời của tôi. Cảm ơn
Marco M.

36

Tôi có một giải pháp 2 dòng cho vấn đề này:

char sep = ' ';
std::string s="1 This is an example";

for(size_t p=0, q=0; p!=s.npos; p=q)
  std::cout << s.substr(p+(p!=0), (q=s.find(sep, p+1))-p-(p!=0)) << std::endl;

Sau đó, thay vì in bạn có thể đặt nó trong một vector.


35

Một cách linh hoạt và nhanh chóng khác

template<typename Operator>
void tokenize(Operator& op, const char* input, const char* delimiters) {
  const char* s = input;
  const char* e = s;
  while (*e != 0) {
    e = s;
    while (*e != 0 && strchr(delimiters, *e) == 0) ++e;
    if (e - s > 0) {
      op(s, e - s);
    }
    s = e + 1;
  }
}

Để sử dụng nó với một vectơ chuỗi (Chỉnh sửa: Vì ai đó đã chỉ ra không kế thừa các lớp STL ... hrmf;)):

template<class ContainerType>
class Appender {
public:
  Appender(ContainerType& container) : container_(container) {;}
  void operator() (const char* s, unsigned length) { 
    container_.push_back(std::string(s,length));
  }
private:
  ContainerType& container_;
};

std::vector<std::string> strVector;
Appender v(strVector);
tokenize(v, "A number of words to be tokenized", " \t");

Đó là nó! Và đó chỉ là một cách để sử dụng mã thông báo, như cách đếm từ:

class WordCounter {
public:
  WordCounter() : noOfWords(0) {}
  void operator() (const char*, unsigned) {
    ++noOfWords;
  }
  unsigned noOfWords;
};

WordCounter wc;
tokenize(wc, "A number of words to be counted", " \t"); 
ASSERT( wc.noOfWords == 7 );

Giới hạn bởi trí tưởng tượng;)



32

Đây là một giải pháp đơn giản chỉ sử dụng thư viện regex tiêu chuẩn

#include <regex>
#include <string>
#include <vector>

std::vector<string> Tokenize( const string str, const std::regex regex )
{
    using namespace std;

    std::vector<string> result;

    sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
    sregex_token_iterator reg_end;

    for ( ; it != reg_end; ++it ) {
        if ( !it->str().empty() ) //token could be empty:check
            result.emplace_back( it->str() );
    }

    return result;
}

Đối số regex cho phép kiểm tra nhiều đối số (dấu cách, dấu phẩy, v.v.)

Tôi thường chỉ kiểm tra để phân chia trên dấu cách và dấu phẩy, vì vậy tôi cũng có chức năng mặc định này:

std::vector<string> TokenizeDefault( const string str )
{
    using namespace std;

    regex re( "[\\s,]+" );

    return Tokenize( str, re );
}

Việc "[\\s,]+"kiểm tra dấu cách ( \\s) và dấu phẩy ( ,).

Lưu ý, nếu bạn muốn chia wstringthay vì string,

  • thay đổi tất cả std::regexthànhstd::wregex
  • thay đổi tất cả sregex_token_iteratorthànhwsregex_token_iterator

Lưu ý, bạn cũng có thể muốn lấy tham số chuỗi theo tham chiếu, tùy thuộc vào trình biên dịch của bạn.


Đây sẽ là câu trả lời yêu thích của tôi, nhưng std :: regex bị hỏng trong GCC 4.8. Họ nói rằng họ đã thực hiện nó một cách chính xác trong GCC 4.9. Tôi vẫn cho bạn +1
mchiasson

1
Đây là mục ưa thích của tôi với các thay đổi nhỏ: vectơ được trả về dưới dạng tham chiếu như bạn đã nói và các đối số "str" ​​và "regex" cũng được truyền bởi các tham chiếu. cám ơn.
QuantumKarl

1
Chuỗi thô là khá hữu ích trong khi xử lý các mẫu regex. Bằng cách đó, bạn không phải sử dụng các chuỗi thoát ... Bạn chỉ có thể sử dụng R"([\s,]+)".
Sam

26

Sử dụng std::stringstreamnhư bạn có hoạt động hoàn toàn tốt, và làm chính xác những gì bạn muốn. Nếu bạn chỉ tìm kiếm cách làm khác nhau, bạn có thể sử dụng std::find()/ std::find_first_of()std::string::substr().

Đây là một ví dụ:

#include <iostream>
#include <string>

int main()
{
    std::string s("Somewhere down the road");
    std::string::size_type prev_pos = 0, pos = 0;

    while( (pos = s.find(' ', pos)) != std::string::npos )
    {
        std::string substring( s.substr(prev_pos, pos-prev_pos) );

        std::cout << substring << '\n';

        prev_pos = ++pos;
    }

    std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last word
    std::cout << substring << '\n';

    return 0;
}

Điều này chỉ hoạt động cho các ký tự phân cách nhân vật duy nhất. Một thay đổi đơn giản cho phép nó hoạt động với multicharacter:prev_pos = pos += delimiter.length();
David Doria

25

Nếu bạn muốn sử dụng boost, nhưng muốn sử dụng toàn bộ chuỗi dưới dạng dấu phân cách (thay vì các ký tự đơn lẻ như trong hầu hết các giải pháp được đề xuất trước đó), bạn có thể sử dụng boost_split_iterator.

Mã ví dụ bao gồm mẫu thuận tiện:

#include <iostream>
#include <vector>
#include <boost/algorithm/string.hpp>

template<typename _OutputIterator>
inline void split(
    const std::string& str, 
    const std::string& delim, 
    _OutputIterator result)
{
    using namespace boost::algorithm;
    typedef split_iterator<std::string::const_iterator> It;

    for(It iter=make_split_iterator(str, first_finder(delim, is_equal()));
            iter!=It();
            ++iter)
    {
        *(result++) = boost::copy_range<std::string>(*iter);
    }
}

int main(int argc, char* argv[])
{
    using namespace std;

    vector<string> splitted;
    split("HelloFOOworldFOO!", "FOO", back_inserter(splitted));

    // or directly to console, for example
    split("HelloFOOworldFOO!", "FOO", ostream_iterator<string>(cout, "\n"));
    return 0;
}

20

Đây là một giải pháp regex chỉ sử dụng thư viện regex tiêu chuẩn. (Tôi hơi yếu, vì vậy có thể có một vài lỗi cú pháp, nhưng ít nhất đây là ý tưởng chung)

#include <regex.h>
#include <string.h>
#include <vector.h>

using namespace std;

vector<string> split(string s){
    regex r ("\\w+"); //regex matches whole words, (greedy, so no fragment words)
    regex_iterator<string::iterator> rit ( s.begin(), s.end(), r );
    regex_iterator<string::iterator> rend; //iterators to iterate thru words
    vector<string> result<regex_iterator>(rit, rend);
    return result;  //iterates through the matches to fill the vector
}

Phản ứng tương tự với cách tiếp cận regex có thể tốt hơn: ở đâyở đây .
tộc

20

Có một chức năng được đặt tên strtok.

#include<string>
using namespace std;

vector<string> split(char* str,const char* delim)
{
    char* saveptr;
    char* token = strtok_r(str,delim,&saveptr);

    vector<string> result;

    while(token != NULL)
    {
        result.push_back(token);
        token = strtok_r(NULL,delim,&saveptr);
    }
    return result;
}

3
strtoklà từ thư viện chuẩn C, không phải C ++. Nó không an toàn để sử dụng trong các chương trình đa luồng. Nó sửa đổi chuỗi đầu vào.
Kevin Panko

13
Bởi vì nó lưu con trỏ char từ cuộc gọi đầu tiên trong một biến tĩnh, do đó, trong các cuộc gọi tiếp theo khi NULL được thông qua, nó sẽ nhớ con trỏ nào sẽ được sử dụng. Nếu một luồng thứ hai gọi strtokkhi một luồng khác vẫn đang xử lý, con trỏ char này sẽ bị ghi đè và cả hai luồng sẽ có kết quả không chính xác. mkssoftware.com/docs/man3/strtok.3.asp
Kevin Panko

1
như đã đề cập trước khi strtok không an toàn và thậm chí trong C strtok_r được khuyến nghị sử dụng
systemfault

4
strtok_r có thể được sử dụng nếu bạn đang ở trong một phần của mã có thể được truy cập. đây là giải pháp duy nhất cho tất cả những điều trên không phải là "nhiễu đường truyền" và là một minh chứng cho điều chính xác là sai với c ++
Erik Aronesty

Được cập nhật để không thể có sự phản đối nào với lý do an toàn luồng từ won C ++.
Erik Aronesty

17

Các stringstream có thể được thuận tiện nếu bạn cần phải phân tích chuỗi bằng các ký hiệu không gian:

string s = "Name:JAck; Spouse:Susan; ...";
string dummy, name, spouse;

istringstream iss(s);
getline(iss, dummy, ':');
getline(iss, name, ';');
getline(iss, dummy, ':');
getline(iss, spouse, ';')

14

Cho đến nay tôi đã sử dụng một trong Boost , nhưng tôi cần một cái gì đó không phụ thuộc vào nó, vì vậy tôi đã đến đây:

static void Split(std::vector<std::string>& lst, const std::string& input, const std::string& separators, bool remove_empty = true)
{
    std::ostringstream word;
    for (size_t n = 0; n < input.size(); ++n)
    {
        if (std::string::npos == separators.find(input[n]))
            word << input[n];
        else
        {
            if (!word.str().empty() || !remove_empty)
                lst.push_back(word.str());
            word.str("");
        }
    }
    if (!word.str().empty() || !remove_empty)
        lst.push_back(word.str());
}

Một điểm tốt là trong separators bạn có thể vượt qua nhiều hơn một nhân vật.


13

Tôi đã tự cuộn bằng strtok và sử dụng boost để tách chuỗi. Phương pháp tốt nhất mà tôi đã tìm thấy là Thư viện bộ công cụ chuỗi C ++ . Nó là vô cùng linh hoạt và nhanh chóng.

#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>

const char *whitespace  = " \t\r\n\f";
const char *whitespace_and_punctuation  = " \t\r\n\f;,=";

int main()
{
    {   // normal parsing of a string into a vector of strings
        std::string s("Somewhere down the road");
        std::vector<std::string> result;
        if( strtk::parse( s, whitespace, result ) )
        {
            for(size_t i = 0; i < result.size(); ++i )
                std::cout << result[i] << std::endl;
        }
    }

    {  // parsing a string into a vector of floats with other separators
        // besides spaces

        std::string s("3.0, 3.14; 4.0");
        std::vector<float> values;
        if( strtk::parse( s, whitespace_and_punctuation, values ) )
        {
            for(size_t i = 0; i < values.size(); ++i )
                std::cout << values[i] << std::endl;
        }
    }

    {  // parsing a string into specific variables

        std::string s("angle = 45; radius = 9.9");
        std::string w1, w2;
        float v1, v2;
        if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
        {
            std::cout << "word " << w1 << ", value " << v1 << std::endl;
            std::cout << "word " << w2 << ", value " << v2 << std::endl;
        }
    }

    return 0;
}

Bộ công cụ có tính linh hoạt cao hơn nhiều so với ví dụ đơn giản này cho thấy nhưng tiện ích của nó trong việc phân tích chuỗi thành các phần tử hữu ích là không thể tin được.


13

Ngắn gọn và thanh lịch

#include <vector>
#include <string>
using namespace std;

vector<string> split(string data, string token)
{
    vector<string> output;
    size_t pos = string::npos; // size_t to avoid improbable overflow
    do
    {
        pos = data.find(token);
        output.push_back(data.substr(0, pos));
        if (string::npos != pos)
            data = data.substr(pos + token.size());
    } while (string::npos != pos);
    return output;
}

có thể sử dụng bất kỳ chuỗi nào làm dấu phân cách, cũng có thể được sử dụng với dữ liệu nhị phân (std :: chuỗi hỗ trợ dữ liệu nhị phân, bao gồm cả null)

sử dụng:

auto a = split("this!!is!!!example!string", "!!");

đầu ra:

this
is
!example!string

1
Tôi thích giải pháp này vì nó cho phép dấu phân tách là một chuỗi chứ không phải char, tuy nhiên, nó đang sửa đổi tại chỗ của chuỗi, vì vậy nó buộc phải tạo một bản sao của chuỗi gốc.
Alessandro Teruzzi

11

Tôi đã làm điều này bởi vì tôi cần một cách dễ dàng để phân tách chuỗi và chuỗi dựa trên c ... Hy vọng rằng ai đó cũng có thể tìm thấy nó hữu ích. Ngoài ra, nó không phụ thuộc vào mã thông báo và bạn có thể sử dụng các trường làm dấu phân cách, đây là một khóa khác tôi cần.

Tôi chắc chắn rằng có những cải tiến có thể được thực hiện để cải thiện hơn nữa sự thanh lịch của nó và làm ơn bằng mọi cách

StringSplitter.hpp:

#include <vector>
#include <iostream>
#include <string.h>

using namespace std;

class StringSplit
{
private:
    void copy_fragment(char*, char*, char*);
    void copy_fragment(char*, char*, char);
    bool match_fragment(char*, char*, int);
    int untilnextdelim(char*, char);
    int untilnextdelim(char*, char*);
    void assimilate(char*, char);
    void assimilate(char*, char*);
    bool string_contains(char*, char*);
    long calc_string_size(char*);
    void copy_string(char*, char*);

public:
    vector<char*> split_cstr(char);
    vector<char*> split_cstr(char*);
    vector<string> split_string(char);
    vector<string> split_string(char*);
    char* String;
    bool do_string;
    bool keep_empty;
    vector<char*> Container;
    vector<string> ContainerS;

    StringSplit(char * in)
    {
        String = in;
    }

    StringSplit(string in)
    {
        size_t len = calc_string_size((char*)in.c_str());
        String = new char[len + 1];
        memset(String, 0, len + 1);
        copy_string(String, (char*)in.c_str());
        do_string = true;
    }

    ~StringSplit()
    {
        for (int i = 0; i < Container.size(); i++)
        {
            if (Container[i] != NULL)
            {
                delete[] Container[i];
            }
        }
        if (do_string)
        {
            delete[] String;
        }
    }
};

StringSplitter.cpp:

#include <string.h>
#include <iostream>
#include <vector>
#include "StringSplit.hpp"

using namespace std;

void StringSplit::assimilate(char*src, char delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.push_back(x);
            }

        }
        else
        {
            delete[] temp;
        }
    }
}

void StringSplit::assimilate(char*src, char* delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.push_back(x);
            }
        }
        else
        {
            delete[] temp;
        }
    }
}

long StringSplit::calc_string_size(char* _in)
{
    long i = 0;
    while (*_in++)
    {
        i++;
    }
    return i;
}

bool StringSplit::string_contains(char* haystack, char* needle)
{
    size_t len = calc_string_size(needle);
    size_t lenh = calc_string_size(haystack);
    while (lenh--)
    {
        if (match_fragment(haystack + lenh, needle, len))
        {
            return true;
        }
    }
    return false;
}

bool StringSplit::match_fragment(char* _src, char* cmp, int len)
{
    while (len--)
    {
        if (*(_src + len) != *(cmp + len))
        {
            return false;
        }
    }
    return true;
}

int StringSplit::untilnextdelim(char* _in, char delim)
{
    size_t len = calc_string_size(_in);
    if (*_in == delim)
    {
        _in += 1;
        return len - 1;
    }

    int c = 0;
    while (*(_in + c) != delim && c < len)
    {
        c++;
    }

    return c;
}

int StringSplit::untilnextdelim(char* _in, char* delim)
{
    int s = calc_string_size(delim);
    int c = 1 + s;

    if (!string_contains(_in, delim))
    {
        return calc_string_size(_in);
    }
    else if (match_fragment(_in, delim, s))
    {
        _in += s;
        return calc_string_size(_in);
    }

    while (!match_fragment(_in + c, delim, s))
    {
        c++;
    }

    return c;
}

void StringSplit::copy_fragment(char* dest, char* src, char delim)
{
    if (*src == delim)
    {
        src++;
    }

    int c = 0;
    while (*(src + c) != delim && *(src + c))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

void StringSplit::copy_string(char* dest, char* src)
{
    int i = 0;
    while (*(src + i))
    {
        *(dest + i) = *(src + i);
        i++;
    }
}

void StringSplit::copy_fragment(char* dest, char* src, char* delim)
{
    size_t len = calc_string_size(delim);
    size_t lens = calc_string_size(src);

    if (match_fragment(src, delim, len))
    {
        src += len;
        lens -= len;
    }

    int c = 0;
    while (!match_fragment(src + c, delim, len) && (c < lens))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

vector<char*> StringSplit::split_cstr(char Delimiter)
{
    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char Delimiter)
{
    do_string = true;

    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

vector<char*> StringSplit::split_cstr(char* Delimiter)
{
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while(*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String,Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char* Delimiter)
{
    do_string = true;
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while (*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

Ví dụ:

int main(int argc, char*argv[])
{
    StringSplit ss = "This:CUT:is:CUT:an:CUT:example:CUT:cstring";
    vector<char*> Split = ss.split_cstr(":CUT:");

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

Sẽ xuất:

Đây

một
ví dụ
cstring

int main(int argc, char*argv[])
{
    StringSplit ss = "This:is:an:example:cstring";
    vector<char*> Split = ss.split_cstr(':');

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This[SPLIT]is[SPLIT]an[SPLIT]example[SPLIT]string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string("[SPLIT]");

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This|is|an|example|string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string('|');

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

Để giữ các mục trống (theo mặc định trống sẽ được loại trừ):

StringSplit ss = mystring;
ss.keep_empty = true;
vector<string> Split = ss.split_string(":DELIM:");

Mục tiêu là làm cho nó tương tự như phương thức Split () của C # trong đó việc tách một chuỗi dễ dàng như:

String[] Split = 
    "Hey:cut:what's:cut:your:cut:name?".Split(new[]{":cut:"}, StringSplitOptions.None);

foreach(String X in Split)
{
    Console.Write(X);
}

Tôi hy vọng người khác có thể thấy điều này hữu ích như tôi.


10

Cái này thì sao:

#include <string>
#include <vector>

using namespace std;

vector<string> split(string str, const char delim) {
    vector<string> v;
    string tmp;

    for(string::const_iterator i; i = str.begin(); i <= str.end(); ++i) {
        if(*i != delim && i != str.end()) {
            tmp += *i; 
        } else {
            v.push_back(tmp);
            tmp = ""; 
        }   
    }   

    return v;
}

Đây là câu trả lời tốt nhất ở đây, nếu bạn chỉ muốn phân chia trên một ký tự phân cách duy nhất. Câu hỏi ban đầu muốn phân tách trên khoảng trắng, có nghĩa là bất kỳ sự kết hợp nào của một hoặc nhiều khoảng trắng hoặc tab liên tiếp. Bạn đã thực sự trả lời stackoverflow.com/questions/53849
Oktalist

10

Câu trả lời này lấy chuỗi và đặt nó vào một vectơ của chuỗi. Nó sử dụng thư viện boost.

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

9

Đây là một cách khác để làm điều đó ..

void split_string(string text,vector<string>& words)
{
  int i=0;
  char ch;
  string word;

  while(ch=text[i++])
  {
    if (isspace(ch))
    {
      if (!word.empty())
      {
        words.push_back(word);
      }
      word = "";
    }
    else
    {
      word += ch;
    }
  }
  if (!word.empty())
  {
    words.push_back(word);
  }
}

9

Tôi thích sử dụng các phương thức boost / regex cho nhiệm vụ này vì chúng cung cấp sự linh hoạt tối đa để chỉ định các tiêu chí chia tách.

#include <iostream>
#include <string>
#include <boost/regex.hpp>

int main() {
    std::string line("A:::line::to:split");
    const boost::regex re(":+"); // one or more colons

    // -1 means find inverse matches aka split
    boost::sregex_token_iterator tokens(line.begin(),line.end(),re,-1);
    boost::sregex_token_iterator end;

    for (; tokens != end; ++tokens)
        std::cout << *tokens << std::endl;
}

9

Gần đây tôi đã phải chia một từ có vỏ lạc đà thành các từ phụ. Không có dấu phân cách, chỉ có ký tự trên.

#include <string>
#include <list>
#include <locale> // std::isupper

template<class String>
const std::list<String> split_camel_case_string(const String &s)
{
    std::list<String> R;
    String w;

    for (String::const_iterator i = s.begin(); i < s.end(); ++i) {  {
        if (std::isupper(*i)) {
            if (w.length()) {
                R.push_back(w);
                w.clear();
            }
        }
        w += *i;
    }

    if (w.length())
        R.push_back(w);
    return R;
}

Ví dụ: phần này chia "AQueryTrades" thành "A", "Truy vấn" và "Giao dịch". Hàm này hoạt động với các chuỗi hẹp và rộng. Bởi vì nó tôn trọng miền địa phương hiện tại, nó chia "RaumfahrtÜberwachungsVerordnung" thành "Raumfahrt", "berwachungs" và "Verordnung".

Lưu ý std::uppernên được thực sự thông qua như là đối số mẫu chức năng. Sau đó, khái quát hơn từ chức năng này có thể phân chia tại các dấu phân cách như ",", ";"hoặc " "quá.


2
Đã có 2 vòng quay. Điều đó thật tuyệt. Có vẻ như tiếng Anh của tôi phải có nhiều "tiếng Đức". Tuy nhiên, người sửa đổi đã không sửa hai lỗi nhỏ có thể vì dù sao chúng cũng rõ ràng: std::isuppercó thể được thông qua dưới dạng đối số, không phải std::upper. Thứ hai đặt một typenametrước String::const_iterator.
Andreas Spindler

9
#include<iostream>
#include<string>
#include<sstream>
#include<vector>
using namespace std;

    vector<string> split(const string &s, char delim) {
        vector<string> elems;
        stringstream ss(s);
        string item;
        while (getline(ss, item, delim)) {
            elems.push_back(item);
        }
        return elems;
    }

int main() {

        vector<string> x = split("thi is an sample test",' ');
        unsigned int i;
        for(i=0;i<x.size();i++)
            cout<<i<<":"<<x[i]<<endl;
        return 0;
}

9

Sử dụng std::string_viewrange-v3thư viện của Eric Niebler's :

https://wandbox.org/permlink/kW5lwRCL1pxjp2pW

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"
#include "range/v3/algorithm.hpp"

int main() {
    std::string s = "Somewhere down the range v3 library";
    ranges::for_each(s  
        |   ranges::view::split(' ')
        |   ranges::view::transform([](auto &&sub) {
                return std::string_view(&*sub.begin(), ranges::distance(sub));
            }),
        [](auto s) {std::cout << "Substring: " << s << "\n";}
    );
}

Bằng cách sử dụng một forvòng lặp phạm vi thay vì ranges::for_eachthuật toán:

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"

int main()
{
    std::string str = "Somewhere down the range v3 library";
    for (auto s : str | ranges::view::split(' ')
                      | ranges::view::transform([](auto&& sub) { return std::string_view(&*sub.begin(), ranges::distance(sub)); }
                      ))
    {
        std::cout << "Substring: " << s << "\n";
    }
}

Yepp, phạm vi dựa trên trông tốt hơn - Tôi đồng ý
Porsche9II
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.