Parsing (pisahkan) string dalam C ++ menggunakan pembatas string (standar C ++)

363

Saya mengurai string dalam C ++ menggunakan yang berikut ini:

using namespace std;

string parsed,input="text to be parsed";
stringstream input_stringstream(input);

if (getline(input_stringstream,parsed,' '))
{
     // do some processing.
}

Parsing dengan pembatas char tunggal baik-baik saja. Tetapi bagaimana jika saya ingin menggunakan string sebagai pembatas.

Contoh: Saya ingin membagi:

scott>=tiger

dengan >=sebagai pembatas sehingga saya bisa mendapatkan scott dan harimau.

ProgramCrazy
sumber

Jawaban:

576

Anda dapat menggunakan std::string::find()fungsi ini untuk menemukan posisi pembatas string Anda, lalu gunakan std::string::substr()untuk mendapatkan token.

Contoh:

std::string s = "scott>=tiger";
std::string delimiter = ">=";
std::string token = s.substr(0, s.find(delimiter)); // token is "scott"
  • The find(const string& str, size_t pos = 0)fungsi mengembalikan posisi kejadian pertama dari strdalam string, atau nposjika string tidak ditemukan.

  • The substr(size_t pos = 0, size_t n = npos)mengembalikan fungsi substring objek, mulai dari posisi posdan panjang npos.


Jika Anda memiliki beberapa pembatas, setelah Anda mengekstrak satu token, Anda dapat menghapusnya (termasuk pembatas) untuk melanjutkan dengan ekstraksi berikutnya (jika Anda ingin mempertahankan string asli, gunakan saja s = s.substr(pos + delimiter.length());):

s.erase(0, s.find(delimiter) + delimiter.length());

Dengan cara ini Anda dapat dengan mudah melakukan loop untuk mendapatkan setiap token.

Contoh Lengkap

std::string s = "scott>=tiger>=mushroom";
std::string delimiter = ">=";

size_t pos = 0;
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
    token = s.substr(0, pos);
    std::cout << token << std::endl;
    s.erase(0, pos + delimiter.length());
}
std::cout << s << std::endl;

Keluaran:

scott
tiger
mushroom
Vincenzo Pii
sumber
66
Bagi mereka yang tidak ingin memodifikasi string input, lakukansize_t last = 0; size_t next = 0; while ((next = s.find(delimiter, last)) != string::npos) { cout << s.substr(last, next-last) << endl; last = next + 1; } cout << s.substr(last) << endl;
hayk.mart
30
CATATAN: mushroomoutput di luar loop, yaitus = mushroom
Don Larynx
1
Sampel tersebut tidak mengekstraksi token terakhir dari string. Contoh tambang yang mengekstraksi IPV4 dari satu string: <code> size_t last = 0; size_t berikutnya = 0; indeks int = 0; while (index <4) {next = str.find (pembatas, terakhir); auto number = str.substr (terakhir, berikutnya - terakhir); IPv4 [index ++] = atoi (number.c_str ()); terakhir = berikutnya + 1; } </code>
rfog
2
@ hayk.mart Hanya sebuah catatan, yang akan menjadi yang berikut, Anda perlu menambahkan 2 bukan 1 karena ukuran pembatas yang adalah 2 karakter :): std :: string s = "scott> = tiger> = mushroom"; std :: string delimiter = "> ="; size_t last = 0; size_t berikutnya = 0; while ((next = s.find (delimiter, last))! = std :: string :: npos) {std :: cout << s.substr (terakhir, terakhir-terakhir) << std :: endl; last = next + 2; } std :: cout << s.substr (terakhir) << std :: endl;
ervinbosenbacher
Untuk mendapatkan "harimau", gunakan std::string token = s.substr(s.find(delimiter) + 1);, jika Anda yakin itu ada (saya menggunakan +1 panjangnya) ...
gsamaras
64

Metode ini menggunakan std::string::findtanpa mengubah string asli dengan mengingat awal dan akhir token substring sebelumnya.

#include <iostream>
#include <string>

int main()
{
    std::string s = "scott>=tiger";
    std::string delim = ">=";

    auto start = 0U;
    auto end = s.find(delim);
    while (end != std::string::npos)
    {
        std::cout << s.substr(start, end - start) << std::endl;
        start = end + delim.length();
        end = s.find(delim, start);
    }

    std::cout << s.substr(start, end);
}
moswald
sumber
34

Anda dapat menggunakan fungsi berikutnya untuk memisahkan string:

vector<string> split(const string& str, const string& delim)
{
    vector<string> tokens;
    size_t prev = 0, pos = 0;
    do
    {
        pos = str.find(delim, prev);
        if (pos == string::npos) pos = str.length();
        string token = str.substr(prev, pos-prev);
        if (!token.empty()) tokens.push_back(token);
        prev = pos + delim.length();
    }
    while (pos < str.length() && prev < str.length());
    return tokens;
}
Sviatoslav
sumber
5
IMO tidak berfungsi seperti yang diharapkan: split("abc","a")akan mengembalikan vektor atau string tunggal "bc",, di mana saya pikir akan lebih masuk akal jika telah mengembalikan vektor elemen ["", "bc"]. Menggunakan str.split()Python, itu intuitif bagi saya bahwa itu harus mengembalikan string kosong jika delimditemukan di awal atau di akhir, tapi itu hanya pendapat saya. Bagaimanapun, saya hanya berpikir itu harus disebutkan
kyriakosSt
1
Akan sangat menyarankan menghapus if (!token.empty()) mencegah masalah yang disebutkan oleh @kyriakosSt serta masalah lain yang terkait dengan pembatas berturut-turut.
Steve
1
Saya akan menghapus upvote saya jika saya bisa, tetapi SO tidak akan membiarkan saya. Masalah yang diangkat oleh @kyriakosSt adalah masalah, dan menghapus if (!token.empty())sepertinya tidak cukup untuk memperbaikinya.
bhaller
1
@bhaller sniplet ini dirancang persis untuk melewati fragmen kosong. Jika Anda perlu menyimpan yang kosong, saya khawatir Anda perlu menulis implementasi split lainnya. Harap menyarankan Anda untuk mempostingnya di sini untuk kebaikan komunitas.
Sviatoslav
32

Untuk pembatas string

Membagi string berdasarkan pembatas string . Seperti memisahkan string "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih"berdasarkan pembatas string "-+", output akan{"adsf", "qwret", "nvfkbdsj", "orthdfjgh", "dfjrleih"}

#include <iostream>
#include <sstream>
#include <vector>

using namespace std;

// for string delimiter
vector<string> split (string s, string delimiter) {
    size_t pos_start = 0, pos_end, delim_len = delimiter.length();
    string token;
    vector<string> res;

    while ((pos_end = s.find (delimiter, pos_start)) != string::npos) {
        token = s.substr (pos_start, pos_end - pos_start);
        pos_start = pos_end + delim_len;
        res.push_back (token);
    }

    res.push_back (s.substr (pos_start));
    return res;
}

int main() {
    string str = "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih";
    string delimiter = "-+";
    vector<string> v = split (str, delimiter);

    for (auto i : v) cout << i << endl;

    return 0;
}


Keluaran

adsf
qwret
nvfkbdsj
orthdfjgh
dfjrleih




Untuk pembatas karakter tunggal

Pisahkan string berdasarkan pembatas karakter. Seperti memisahkan string "adsf+qwer+poui+fdgh"dengan pembatas "+"akan ditampilkan{"adsf", "qwer", "poui", "fdg"h}

#include <iostream>
#include <sstream>
#include <vector>

using namespace std;

vector<string> split (const string &s, char delim) {
    vector<string> result;
    stringstream ss (s);
    string item;

    while (getline (ss, item, delim)) {
        result.push_back (item);
    }

    return result;
}

int main() {
    string str = "adsf+qwer+poui+fdgh";
    vector<string> v = split (str, '+');

    for (auto i : v) cout << i << endl;

    return 0;
}


Keluaran

adsf
qwer
poui
fdgh
Arafat Hasan
sumber
Anda kembali vector<string>saya pikir itu akan memanggil copy constructor.
Mayur
2
Setiap referensi yang saya lihat menunjukkan bahwa panggilan ke copy constructor dihilangkan dalam konteks itu.
David Diberikan
Dengan kompiler "modern" (C ++ 03?) Saya percaya ini benar, RVO dan / atau memindahkan semantik akan menghilangkan copy constructor.
Kevin
Saya mencoba satu untuk pembatas karakter tunggal, dan jika string berakhir pada pembatas (yaitu, kolom csv kosong di akhir baris), itu tidak mengembalikan string kosong. Ini hanya mengembalikan satu string lebih sedikit. Sebagai contoh: 1,2,3,4 \ nA, B, C,
kounoupis
Saya juga mencoba satu untuk pembatas string, dan jika string berakhir pada pembatas, pembatas terakhir menjadi bagian dari string terakhir yang diekstraksi.
kounoupis
20

Kode ini memisahkan garis dari teks, dan menambahkan semua orang ke dalam vektor.

vector<string> split(char *phrase, string delimiter){
    vector<string> list;
    string s = string(phrase);
    size_t pos = 0;
    string token;
    while ((pos = s.find(delimiter)) != string::npos) {
        token = s.substr(0, pos);
        list.push_back(token);
        s.erase(0, pos + delimiter.length());
    }
    list.push_back(s);
    return list;
}

Dipanggil oleh:

vector<string> listFilesMax = split(buffer, "\n");
William Cuervo
sumber
ini bekerja dengan baik! Saya telah menambahkan list.push_back (s); karena itu hilang.
Stoica Mircea
1
itu merindukan bagian terakhir dari string. Setelah loop sementara berakhir, kita perlu menambahkan sisa s sebagai token baru.
whihathac
Saya telah mengedit kode sampel untuk memperbaiki push_back yang hilang.
fret
1
Akan lebih baikvector<string> split(char *phrase, const string delimiter="\n")
Mayur
15

strtok memungkinkan Anda untuk melewati beberapa karakter sebagai pembatas. Saya bertaruh jika Anda memasukkan "> =" string contoh Anda akan dipisah dengan benar (meskipun> dan = dihitung sebagai pembatas individu).

EDIT jika Anda tidak ingin menggunakan c_str()untuk mengkonversi dari string ke char *, Anda dapat menggunakan substr dan find_first_of untuk tokenize.

string token, mystring("scott>=tiger");
while(token != mystring){
  token = mystring.substr(0,mystring.find_first_of(">="));
  mystring = mystring.substr(mystring.find_first_of(">=") + 1);
  printf("%s ",token.c_str());
}
ryanbwork
sumber
3
Terima kasih. Tapi saya ingin menggunakan hanya C ++ dan tidak ada fungsi C seperti strtok()karena akan mengharuskan saya untuk menggunakan array char bukan string.
TheCrazyProgrammer
2
@TheCrazyProgrammer Jadi? Jika fungsi C melakukan apa yang Anda butuhkan, gunakan itu. Ini bukan dunia di mana fungsi C tidak tersedia di C ++ (sebenarnya, mereka harus). .c_str()murah dan mudah juga.
Qix - MONICA DISALAHKAN
1
Pemeriksaan jika (token! = Mystring) memberikan hasil yang salah jika Anda memiliki elemen berulang dalam string Anda. Saya menggunakan kode Anda untuk membuat versi yang tidak memiliki ini. Ini memiliki banyak perubahan yang mengubah jawaban secara mendasar, jadi saya menulis jawaban saya sendiri alih-alih mengedit. Lihat di bawah.
Amber Elferink
5

Inilah pendapat saya tentang ini. Ini menangani kasus tepi dan mengambil parameter opsional untuk menghapus entri kosong dari hasil.

bool endsWith(const std::string& s, const std::string& suffix)
{
    return s.size() >= suffix.size() &&
           s.substr(s.size() - suffix.size()) == suffix;
}

std::vector<std::string> split(const std::string& s, const std::string& delimiter, const bool& removeEmptyEntries = false)
{
    std::vector<std::string> tokens;

    for (size_t start = 0, end; start < s.length(); start = end + delimiter.length())
    {
         size_t position = s.find(delimiter, start);
         end = position != string::npos ? position : s.length();

         std::string token = s.substr(start, end - start);
         if (!removeEmptyEntries || !token.empty())
         {
             tokens.push_back(token);
         }
    }

    if (!removeEmptyEntries &&
        (s.empty() || endsWith(s, delimiter)))
    {
        tokens.push_back("");
    }

    return tokens;
}

Contohnya

split("a-b-c", "-"); // [3]("a","b","c")

split("a--c", "-"); // [3]("a","","c")

split("-b-", "-"); // [3]("","b","")

split("--c--", "-"); // [5]("","","c","","")

split("--c--", "-", true); // [1]("c")

split("a", "-"); // [1]("a")

split("", "-"); // [1]("")

split("", "-", true); // [0]()
Beder Acosta Borges
sumber
4

Ini harus bekerja dengan sempurna untuk pembatas string (atau karakter tunggal). Jangan lupa untuk memasukkan #include <sstream>.

std::string input = "Alfa=,+Bravo=,+Charlie=,+Delta";
std::string delimiter = "=,+"; 
std::istringstream ss(input);
std::string token;
std::string::iterator it;

while(std::getline(ss, token, *(it = delimiter.begin()))) {
    while(*(++it)) ss.get();
    std::cout << token << " " << '\n';
}

Loop sementara pertama mengekstraksi token menggunakan karakter pertama dari pembatas string. Loop kedua sementara melompati sisa pembatas dan berhenti pada awal token berikutnya.

hmofrad
sumber
3

Saya akan menggunakan boost::tokenizer. Berikut dokumentasi yang menjelaskan cara membuat fungsi tokenizer yang sesuai: http://www.boost.org/doc/libs/1_52_0/libs/tokenizer/tokenizerfunction.htm

Ini salah satu yang sesuai untuk kasus Anda.

struct my_tokenizer_func
{
    template<typename It>
    bool operator()(It& next, It end, std::string & tok)
    {
        if (next == end)
            return false;
        char const * del = ">=";
        auto pos = std::search(next, end, del, del + 2);
        tok.assign(next, pos);
        next = pos;
        if (next != end)
            std::advance(next, 2);
        return true;
    }

    void reset() {}
};

int main()
{
    std::string to_be_parsed = "1) one>=2) two>=3) three>=4) four";
    for (auto i : boost::tokenizer<my_tokenizer_func>(to_be_parsed))
        std::cout << i << '\n';
}
Benjamin Lindley
sumber
3
Terima kasih. Tapi saya ingin hanya berharap C ++ standar dan bukan perpustakaan pihak ketiga.
TheCrazyProgrammer
@TheCrazyProgrammer: Oke, ketika saya membaca "Standard C ++", saya pikir itu berarti tidak ada ekstensi non-standar, bukan berarti Anda tidak bisa menggunakan standar yang sesuai dengan perpustakaan pihak ketiga.
Benjamin Lindley
3

Jawaban sudah ada, tetapi dipilih-jawaban menggunakan fungsi hapus yang sangat mahal, pikirkan beberapa string yang sangat besar (dalam MB). Karena itu saya menggunakan fungsi di bawah ini.

vector<string> split(const string& i_str, const string& i_delim)
{
    vector<string> result;

    size_t found = i_str.find(i_delim);
    size_t startIndex = 0;

    while(found != string::npos)
    {
        string temp(i_str.begin()+startIndex, i_str.begin()+found);
        result.push_back(temp);
        startIndex = found + i_delim.size();
        found = i_str.find(i_delim, startIndex);
    }
    if(startIndex != i_str.size())
        result.push_back(string(i_str.begin()+startIndex, i_str.end()));
    return result;      
}
Shubham Agrawal
sumber
Saya menguji ini, dan itu berhasil. Terima kasih! Menurut pendapat saya, ini adalah jawaban terbaik karena seperti yang dinyatakan sebagai jawaban pertama, solusi ini mengurangi overhead memori, dan hasilnya disimpan dengan nyaman dalam vektor. (meniru string.split()metode Python .)
Robbie Capps
2

Ini adalah metode lengkap yang memisahkan string pada pembatas apa pun dan mengembalikan vektor string yang dicacah.

Ini adalah adaptasi dari jawaban dari ryanbwork. Namun, pemeriksaannya untuk: if(token != mystring)memberikan hasil yang salah jika Anda memiliki elemen berulang dalam string Anda. Ini solusi saya untuk masalah itu.

vector<string> Split(string mystring, string delimiter)
{
    vector<string> subStringList;
    string token;
    while (true)
    {
        size_t findfirst = mystring.find_first_of(delimiter);
        if (findfirst == string::npos) //find_first_of returns npos if it couldn't find the delimiter anymore
        {
            subStringList.push_back(mystring); //push back the final piece of mystring
            return subStringList;
        }
        token = mystring.substr(0, mystring.find_first_of(delimiter));
        mystring = mystring.substr(mystring.find_first_of(delimiter) + 1);
        subStringList.push_back(token);
    }
    return subStringList;
}
Amber Elferink
sumber
1
Sesuatu seperti while (true)ini biasanya menakutkan untuk dilihat dalam kode seperti ini. Secara pribadi saya akan merekomendasikan menulis ulang ini sehingga perbandingan std::string::npos(atau masing-masing cek terhadap mystring.size()) membuat while (true)usang.
Joel Bodenmann
1

Jika Anda tidak ingin memodifikasi string (seperti dalam jawaban oleh Vincenzo Pii) dan ingin menampilkan token terakhir juga, Anda mungkin ingin menggunakan pendekatan ini:

inline std::vector<std::string> splitString( const std::string &s, const std::string &delimiter ){
    std::vector<std::string> ret;
    size_t start = 0;
    size_t end = 0;
    size_t len = 0;
    std::string token;
    do{ end = s.find(delimiter,start); 
        len = end - start;
        token = s.substr(start, len);
        ret.emplace_back( token );
        start += len + delimiter.length();
        std::cout << token << std::endl;
    }while ( end != std::string::npos );
    return ret;
}
pengguna2366975
sumber
0
#include<iostream>
#include<algorithm>
using namespace std;

int split_count(string str,char delimit){
return count(str.begin(),str.end(),delimit);
}

void split(string str,char delimit,string res[]){
int a=0,i=0;
while(a<str.size()){
res[i]=str.substr(a,str.find(delimit));
a+=res[i].size()+1;
i++;
}
}

int main(){

string a="abc.xyz.mno.def";
int x=split_count(a,'.')+1;
string res[x];
split(a,'.',res);

for(int i=0;i<x;i++)
cout<<res[i]<<endl;
  return 0;
}

PS: Hanya berfungsi jika panjang senar setelah pemisahan sama


sumber
Ini menggunakan ekstensi GCC - array panjang variabel.
user202729
0

Fungsi:

std::vector<std::string> WSJCppCore::split(const std::string& sWhat, const std::string& sDelim) {
    std::vector<std::string> vRet;
    int nPos = 0;
    int nLen = sWhat.length();
    int nDelimLen = sDelim.length();
    while (nPos < nLen) {
        std::size_t nFoundPos = sWhat.find(sDelim, nPos);
        if (nFoundPos != std::string::npos) {
            std::string sToken = sWhat.substr(nPos, nFoundPos - nPos);
            vRet.push_back(sToken);
            nPos = nFoundPos + nDelimLen;
            if (nFoundPos + nDelimLen == nLen) { // last delimiter
                vRet.push_back("");
            }
        } else {
            std::string sToken = sWhat.substr(nPos, nLen - nPos);
            vRet.push_back(sToken);
            break;
        }
    }
    return vRet;
}

Unit-tes:

bool UnitTestSplit::run() {
bool bTestSuccess = true;

    struct LTest {
        LTest(
            const std::string &sStr,
            const std::string &sDelim,
            const std::vector<std::string> &vExpectedVector
        ) {
            this->sStr = sStr;
            this->sDelim = sDelim;
            this->vExpectedVector = vExpectedVector;
        };
        std::string sStr;
        std::string sDelim;
        std::vector<std::string> vExpectedVector;
    };
    std::vector<LTest> tests;
    tests.push_back(LTest("1 2 3 4 5", " ", {"1", "2", "3", "4", "5"}));
    tests.push_back(LTest("|1f|2п|3%^|44354|5kdasjfdre|2", "|", {"", "1f", "2п", "3%^", "44354", "5kdasjfdre", "2"}));
    tests.push_back(LTest("|1f|2п|3%^|44354|5kdasjfdre|", "|", {"", "1f", "2п", "3%^", "44354", "5kdasjfdre", ""}));
    tests.push_back(LTest("some1 => some2 => some3", "=>", {"some1 ", " some2 ", " some3"}));
    tests.push_back(LTest("some1 => some2 => some3 =>", "=>", {"some1 ", " some2 ", " some3 ", ""}));

    for (int i = 0; i < tests.size(); i++) {
        LTest test = tests[i];
        std::string sPrefix = "test" + std::to_string(i) + "(\"" + test.sStr + "\")";
        std::vector<std::string> vSplitted = WSJCppCore::split(test.sStr, test.sDelim);
        compareN(bTestSuccess, sPrefix + ": size", vSplitted.size(), test.vExpectedVector.size());
        int nMin = std::min(vSplitted.size(), test.vExpectedVector.size());
        for (int n = 0; n < nMin; n++) {
            compareS(bTestSuccess, sPrefix + ", element: " + std::to_string(n), vSplitted[n], test.vExpectedVector[n]);
        }
    }

    return bTestSuccess;
}
laut-kg
sumber
0
std::vector<std::string> parse(std::string str,std::string delim){
    std::vector<std::string> tokens;
    char *str_c = strdup(str.c_str()); 
    char* token = NULL;

    token = strtok(str_c, delim.c_str()); 
    while (token != NULL) { 
        tokens.push_back(std::string(token));  
        token = strtok(NULL, delim.c_str()); 
    }

    delete[] str_c;

    return tokens;
}
XLVII
sumber
-4
std::vector<std::string> split(const std::string& s, char c) {
  std::vector<std::string> v;
  unsigned int ii = 0;
  unsigned int j = s.find(c);
  while (j < s.length()) {
    v.push_back(s.substr(i, j - i));
    i = ++j;
    j = s.find(c, j);
    if (j >= s.length()) {
      v.push_back(s.substr(i, s,length()));
      break;
    }
  }
  return v;
}
Yilei
sumber
1
Harap lebih akurat. Kode Anda tidak akan dikompilasi. Lihat deklarasi "i" dan koma bukan titik.
jstuardo