Babiker
Babiker

Reputation: 18798

How can I split a string by a delimiter into an array?

I am new to programming. I have been trying to write a function in C++ that explodes the contents of a string into a string array at a given parameter, example:

string str = "___this_ is__ th_e str__ing we__ will use__";

should return string array:

cout << stringArray[0]; // 'this'
cout << stringArray[1]; // ' is'
cout << stringArray[2]; // ' th'
cout << stringArray[3]; // 'e str'
cout << stringArray[4]; // 'ing we'
cout << stringArray[5]; // ' will use'

I can tokenize the string just fine, but the hardest part for me is how can i specify the number of elements in stringArray before assigning it the current string toke and also how to return stringArray from the function.

Would someone show me how to write the function?

Edit1: I don't necessarily need the results to been in string array just any container that i can call as a regular variable with some sort of indexing.

Upvotes: 16

Views: 78737

Answers (13)

Loki Astari
Loki Astari

Reputation: 264351

Using STL (sorry no compiler not tested)

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

int main()
{
    std::vector<std::string>   result;

    std::string str = "___this_ is__ th_e str__ing we__ will use__";

    std::stringstream  data(str);

    std::string line;
    while(std::getline(data,line,'_'))
    {
        result.push_back(line); // Note: You may get a couple of blank lines
                                // When multiple underscores are beside each other.
    }
}

// or define a token

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

struct Token: public std::string  // Yes I know this is nasty.
{                                 // But it is just to demosntrate the principle.    
};

std::istream& operator>>(std::istream& s,Token& t)
{
    std::getline(s,t,'_');
   
    // *** 
    // Remove extra '_' characters from the stream.
    char c;
    while(s && ((c = s.get()) != '_')) {/*Do Nothing*/}
    if (s)
    {
        s.unget(); // Put back the last char as it is not '_'
    }
    return s;
}

int main()
{   

    std::string str = "___this_ is__ th_e str__ing we__ will use__";

    std::stringstream  data(str);

    std::vector<std::string>   result(std::istream_iterator<Token>(data),
                                      std::istream_iterator<Token>());
}

Upvotes: 11

Konchog
Konchog

Reputation: 2188

# turn a string into a deque based on a delimiter string
bool tolist(deque<string>& list,string basis,const string& cutter) {
    bool found = false;
    if (!cutter.empty()) {
        while (!basis.empty() ) {
            string::size_type pos = basis.find(cutter);
            if (pos != string::npos) {
                found = true;
                list.push_back(basis.substr(0, pos)); //pos says 2
                basis = basis.substr(pos+cutter.size(),string::npos);
            } else {
                list.push_back(basis);
                basis.clear();
            }
        }
    }
    return found;
}

Upvotes: 0

Mukesh Chapagain
Mukesh Chapagain

Reputation: 25948

This worked for me:

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

using namespace std;

vector<string> split(string str, char delimiter) {
  vector<string> internal;
  stringstream ss(str); // Turn the string into a stream.
  string tok;

  while(getline(ss, tok, delimiter)) {
    internal.push_back(tok);
  }

  return internal;
}

int main(int argc, char **argv) {
  string myCSV = "one,two,three,four";
  vector<string> sep = split(myCSV, ',');

  // If using C++11 (which I recommend)
  /* for(string t : sep)
   *  cout << t << endl;
   */

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

Source: http://code.runnable.com/VHb0hWMZp-ws1gAr/splitting-a-string-into-a-vector-for-c%2B%2B

Upvotes: 0

InusualZ
InusualZ

Reputation: 59

I think that I have wrote a much simpler solution.

std::vector<std::string> explode(const std::string& string, const char delimiter) {

std::vector<std::string> result;
unsigned int start = 0, pos = 0;

while (pos != string.length()) {
    if (string.at(pos) == delimiter || pos + 1 == string.length()) {
        unsigned int size = (pos - start) + ((pos + 1) == string.length() ? 1 : 0);
        if (size != 0) { // Make this 'if' as a option? like a parameter with removeEmptyString?
            result.push_back(string.substr(start, size));
        }
        start = pos + 1;
    }
    pos++;
}

return std::move(result);

}

Upvotes: 0

Vivek
Vivek

Reputation: 3613

Here is my cooked up code (complete). May be it's useful for some with the same need.

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

int main(){
        std::string s = "scott:tiger:mushroom";
        std::string delimiter = ":";

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

        // Printing Array to see the results
        std::cout<<"====================================================================================\n";
        for ( int i=0;i<outputArr.size();i++){
                std::cout<<outputArr[i]<<"\n";
        }
        std::cout<<"====================================================================================\n";
}

Cheers!!

Upvotes: 0

Eric Scrivner
Eric Scrivner

Reputation: 1849

Here's my first attempt at this using vectors and strings:

vector<string> explode(const string& str, const char& ch) {
    string next;
    vector<string> result;

    // For each character in the string
    for (string::const_iterator it = str.begin(); it != str.end(); it++) {
        // If we've hit the terminal character
        if (*it == ch) {
            // If we have some characters accumulated
            if (!next.empty()) {
                // Add them to the result vector
                result.push_back(next);
                next.clear();
            }
        } else {
            // Accumulate the next character into the sequence
            next += *it;
        }
    }
    if (!next.empty())
         result.push_back(next);
    return result;
}

Hopefully this gives you some sort of idea of how to go about this. On your example string it returns the correct results with this test code:

int main (int, char const **) {
    std::string blah = "___this_ is__ th_e str__ing we__ will use__";
    std::vector<std::string> result = explode(blah, '_');

    for (size_t i = 0; i < result.size(); i++) {
        cout << "\"" << result[i] << "\"" << endl;
    }
    return 0;
}

Upvotes: 16

Ouadie
Ouadie

Reputation: 13185

It works for me :

#include <iostream>
#include <vector>
#include <string>

using namespace std;

vector<string> explode( const string &delimiter, const string &explodeme);

int main(int argc, char *argv[])
{
    string str = "I have a lovely bunch of cocoa nuts";
    cout<<str<<endl;
    vector<string> v = explode(" ", str);
    for(int i=0; i<v.size(); i++)
        cout <<i << " ["<< v[i] <<"] " <<endl;
}

vector<string> explode( const string &delimiter, const string &str)
{
    vector<string> arr;

    int strleng = str.length();
    int delleng = delimiter.length();
    if (delleng==0)
        return arr;//no change

    int i=0;
    int k=0;
    while( i<strleng )
    {
        int j=0;
        while (i+j<strleng && j<delleng && str[i+j]==delimiter[j])
            j++;
        if (j==delleng)//found delimiter
        {
            arr.push_back(  str.substr(k, i-k) );
            i+=delleng;
            k=i;
        }
        else
        {
            i++;
        }
    }
    arr.push_back(  str.substr(k, i-k) );
    return arr;
}

source : http://www.zedwood.com/article/106/cpp-explode-function

Upvotes: 3

Paulo Lellis
Paulo Lellis

Reputation: 1

The code below:

template <typename OutputIterator>
int explode(const string &s, const char c, OutputIterator output) {
    stringstream  data(s);
    string line;
    int i=0;
    while(std::getline(data,line,c)) { *output++ = line; i++; }
    return i;
}

int main(...) {
  string test="H:AMBV4:2:182.45:182.45:182.45:182.45:182.41:32:17700:3229365:201008121711:0";
  cout << test << endl; 
  vector<string> event;
**This is the main call**
  int evts = explode(test,':', back_inserter(event));
  for (int k=0; k<evts; k++) 
    cout << event[k] << "~";
  cout << endl;
}

Outputs

H:AMBV4:2:182.45:182.45:182.45:182.45:182.41:32:17700:3229365:201008121711:0
H~AMBV4~2~182.45~182.45~182.45~182.45~182.41~32~17700~3229365~201008121711~0~

Upvotes: 0

JasonRShaver
JasonRShaver

Reputation: 4394

Wait until your data structures class and then code it with a linked list. If it is for homework though, you may be able to get away with just initing the array be very large.

Upvotes: 0

If you insist on making stringArray an array as oppossed to a std::vector<> (which would be the right thing to do) you have to either:

  1. Make two passes (one to count, you see)
  2. Implement a dynamic array yourself.

Using a vector is easier vector::push_back() appends new stuff to the end. So:

vector* explode(string s){
  vector<string> *v = new vector<string>
  //...
  // in a loop
    v->push_back(string_fragment);
  //...
  return v;
}

Not needed after all Left in for completeness.

To return the array of strings you use char **.

As in

char ** explode(const char *in){
  ...

}

BTW-- How will the calling function know how many elements are in the returned array? You'll have to solve that too. Use std::vector<> unless you are constrained by outside forces...

Upvotes: 1

user79755
user79755

Reputation: 2731

Perhaps you should use a list instead of an array. That way you would not need to know the number of elements ahead of time. You may also consider using the STL containers.

Upvotes: 1

workmad3
workmad3

Reputation: 25667

Use std::vector as a dynamic array and return that as your result.

Upvotes: 1

Jem
Jem

Reputation: 2275

You can use a vector of string (std::vector<std::string>), append each token to it with push_back, and then return it from your tokenize function.

Upvotes: 1

Related Questions