Replace a loop with strtok by using Standard Library

Viewed 318

I have a problem that strtok solves (split substrings from a string) but I realize that strtok is not safe. I want to use some more modern parts of the C++ standard library.

What should I use instead?

static int ParseLine(std::string line, 
                     std::string seps, 
                     int startIdx, 
                     std::vector<CNode>& collection)
{
    if (startIdx > collection.size())
    {
        throw std::invalid_argument("the start index is out of range");
    }
    char buf[2000];
    strcpy_s(buf, line.c_str());
    auto idx = startIdx;
    for (auto objectType = strtok(buf, seps.c_str()); objectType != nullptr; idx++)
    {
        if (idx == collection.size())
        {
            collection.push_back(CNode(idx));
        }
        collection[idx].SetObjectType(objectType);
        objectType = strtok(nullptr, seps.c_str());
    }
    return (idx - 1);
}

Here a complete sample that compiles with _CRT_SECURE_NO_WARNINGS:

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

class CObject
{
    std::string _objectType;

public:
                CObject() : _objectType("n/a") {}
    void        SetObjectType(std::string objectType) { _objectType = objectType; }
    std::string GetObjectType() const { return _objectType; }

};

class CNode
{
    int     _id;
    CObject _object;

public:
    explicit CNode(int id) : _id(id) {}
    void        SetObjectType(std::string objectType) { _object.SetObjectType(objectType); }
    std::string GetObjectType() const { return _object.GetObjectType(); }

};

// Update the collection of nodes with the object types found in the line specified.
// Return the number of elements in the collection.
static int ParseLine(std::string line, std::string seps, int startIdx, std::vector<CNode>& collection)
{
    if (startIdx > collection.size())
    {
        throw std::invalid_argument("the start index is out of range");
    }
    char buf[2000];
    strcpy_s(buf, line.c_str());
    auto idx = startIdx;
    for (auto objectType = strtok(buf, seps.c_str()); objectType != nullptr; idx++)
    {
        if (idx == collection.size())
        {
            collection.push_back(CNode(idx));
        }
        collection[idx].SetObjectType(objectType);
        objectType = strtok(nullptr, seps.c_str());
    }
    return (idx - 1);
}

int main()
{
    std::string seps = ".";
    //                   2   3   4   5   6   7   8   9
    std::string line = "abc.def.ghi.klm.nop.qrs.tuv.wxyz";
    std::vector<CNode> collection { CNode(0), CNode(1), CNode(2) , CNode(3) , CNode(4) , CNode(5) };
    auto startAt = 2;
    try
    {
        auto collection_size = ParseLine(line, seps, startAt, collection);
        std::cout << collection_size << std::endl;
        for (auto value : collection)
        {
            std::cout << value.GetObjectType() << std::endl;
        }
    }
    catch (std::invalid_argument& e)
    {
        std::cout << " out of range exception " << e.what() << std::endl;
    }
    return 0;
}
3 Answers
Related