Hi!
I am writing my own string splitter and i'm stuck.
When I'm trying to split the same string the second time im getting only the first word. Im using Visual Studio 2013.

"Name|Phone Number|Account Number|Price|Tariff"

Output is :

Name
Phone Number
Account Number
Price
Tariff
Name

and my desired output is:

Name
Phone Number
Account Number
Price
Tariff
Name
Phone Number
Account Number
Price
Tariff

My Code:

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <ostream>
#include <map>
#include <vector>
#include <string>
using namespace std;

int StrTok(vector<string> &vTokens, char* string, char *delim);

int main()
{
    char *p_str;
    char str[255] = "Name|Phone Number|Account Number|Price|Tariff|6079777";
    char delim[10] = "|";

    vector<string> vStr_2;


    for (int j = 0; j < 2; j++)
    {
    StrTok(vStr_2, str, delim);
    }


    for (int i = 0; i < vStr_2.size(); i++){
        cout << vStr_2[i] << "\n";
    }
    return 0;
}

int StrTok(vector<string> &vTokens, char* string, char *delim)
{
    static char *ptr;
    const char *p_delim;
    char *token;
    int i = 0;

    if (*string != '\0'){
        ptr = string;
        token = string;
    }
    else
    {
        return vTokens.size();
    }

    for (; ptr != NULL; ptr = NULL, i++){

        while (*ptr != '\0')
        {
            p_delim = delim;

            while (*p_delim != '\0')
            {

                if (*ptr == *p_delim)
                {


                    *ptr = '\0';
                    ptr++;

                    vTokens.push_back(token);
                    token = ptr;
                }
                else{

                    p_delim++;
                }
            }

            ptr++;
        }

        vTokens.push_back(token);
    }

    return vTokens.size();
}

Im asking for help to get my desired output.

Thanks in advanced.

This right here is your problem:

*ptr = '\0'

...because you do this earlier:

ptr = string

...which means you're modifying the string as you scan it. This is not a good idea.

A better signature for your splitter function would be:

int StrTok(vector<string> &vTokens, const char* string, const char *delim);

Note the use of const; now the compiler won't let you change those arguments.

This breaks your code, though, so you'll have to work out how to split the string without modifying it. You could make a copy, but I think a better approach would be to track the start and end positions of the "current" token, advancing them as you find the delimiter string.

Unrelated comments:
* ptr doesn't need to be static.
* i should be a size_t to avoid the signed/unsigned mismatch warning.
* p_str isn't used anywhere.

You could do this fairly easily with a stringstream and getline(). The split function would look like:

std::vector<std::string> split_string(const std::string & line, char delim = ' ')
{
    std::vector<std::string> sep;
    std::string token;
    std::stringstream ss(line);
    while(getline(ss, token, delim))
        sep.push_back(token);
    return sep;
}

And then you could use it like

int main ()
{
    std::vector<std::string> sep = split_string("Name|Phone Number|Account Number|Price|Tariff|6079777", '|');
    for(const auto & e : sep)
        std::cout << e << std::endl;
    return 0;
}

See this Live Example

Edited 1 Year Ago by NathanOliver

Thanks for all your replies.
I'm making a copy of the string as gusano 79 suggested, and it works find, but when my string is ending with a delimiter my program crashes.

Example: "Name|Phone Number|Account Number|Price|Tariff|6079777|"

My assignment is to work with pointers. So i'm banging my head with the pointers.

int StrTok(vector<string> &vTokens, char* string, char *delim)
{
    char *ptr;
    const char *p_delim;
    char *token;
    int i = 0;
    char *cBuf = new char[strlen(string)+1];

    if (*string != '\0'){
        memcpy(cBuf, string, strlen(string) + 1);
        ptr = cBuf;
        token = cBuf;
    }
    else
    {
        return vTokens.size();
    }

    for (; ptr != NULL; ptr = NULL, i++){

        while (*ptr != '\0')
        {
            p_delim = delim;

            while (*p_delim != '\0')
            {

                if (*ptr == *p_delim)
                {


                    *ptr = '\0';
                    ptr++;

                    vTokens.push_back(token);
                    token = ptr;
                }
                else{

                    p_delim++;
                }
            }

            ptr++;
        }

        vTokens.push_back(token);
    }

    delete[] cBuf;
    return vTokens.size();
}

Your code would be so much more C++ like if you could follow the example of Dani's @NathanOliver ...

But if you really have to use C strings and pointers to char ... (in a C++ program) ...you could try something like this:

// split2.cpp //

#include <iostream>
#include <sstream> // re . istringstream object //
#include <string>
#include <vector>

#include <cstdlib> // malloc
#include <cstring> // re. strchr, etc... //

using namespace std;

// using stringstream objects //
void toToks( vector< string >& v, const string& line, const char delimit = '|' )
{
    istringstream iss( line ); // construct istringstring object 'iss' from string 'line'
    string tmpTok;
    while( getline( iss, tmpTok, delimit ) )
        v.push_back( tmpTok ); // push back a copy //
}

// using strchr and passing in C strings //
void toToks2( vector< string >& v, const char* line, const char delimit = '|' )
{
    const char* p1 = strchr( line, delimit );

    while( p1 )
    {
        int size = p1-line;
        char* str = (char*) malloc (sizeof(char) * (size+1));
        if( !str )
        {
            cout << "\nAn ERROR occured in calling malloc ...\n\n";
            return;
        }
        strncpy( str, line, size );
        str[size] = 0;

        v.push_back( str ); // push back a C++ string copy //
        free( str );

        while( *p1 && *p1 == delimit ) ++ p1;
        if( *p1 )
        {
            line = p1;
            p1 = strchr( line, delimit );
        }
        else break;
    }
}



int main ()
{
    cout << "Preferred 'method 1' using C++ stringstream objects ...\n";

    vector< string > toks, toks2; // get empty vectors to hold string tokens //

    // load up vector ...
    toToks( toks, "Name1|Phone Number1|Account Number1|Price1|Tariff1|6079777|" );
    toToks( toks, "Name2|Phone Number2|Account Number2|Price2|Tariff2|6079778|" );
    toToks( toks, "Name3|Phone Number3|Account Number3|Price3|Tariff3|6079779|" );

    // show each parsed line ... with tokens 1..6 //
    for( size_t i = 0; i < toks.size(); ++ i )
    {
        cout << '(' << (i%6+1) << ") " << toks[i] << '\n';
        if( (i + 1) % 6 == 0 ) cout << '\n';
    }


    cout << "'Method 2' using C string strchr and tmp dynamic C strings using malloc...\n";

    // load up vector ...
    toToks2( toks2, "Name11|Phone Number11|Account Number11|Price11|Tariff11|6079777|" );
    toToks2( toks2, "Name22|Phone Number22|Account Number22|Price22|Tariff22|6079778|" );
    toToks2( toks2, "Name33|Phone Number33|Account Number33|Price33|Tariff33|6079779|" );

    // show each parsed line ... with tokens 1..6 //
    for( size_t i = 0; i < toks2.size(); ++ i )
    {
        cout << '(' << (i%6+1) << ") " << toks2[i] << '\n';
        if( (i + 1) % 6 == 0 ) cout << '\n';
    }
}

Edited 1 Year Ago by David W

This question has already been answered. Start a new discussion instead.