2017-05-12 51 views
5

我有一個問題,即strtok解決(從字符串拆分子串),但我 認識到strtok是不安全的。我想使用C++標準庫的一些更現代的部分。使用標準庫替換循環strtok

我應該用什麼來代替?

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); 
} 

這裏與 _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; 
} 
+0

你怎麼舒服是使用正則表達式? – Charles

+0

感謝您的提示,我從來沒有使用正則表達式,但我會在這個方向上尋求解決方案。 –

回答

3

這裏是分割分隔字符串的兩個例子。

第一個使用std::getline的字符串流,指定分隔符而不是使用默認換行符。這種技術只能使用單字符分隔符。

第二個示例使用<regex>庫,該庫允許任意長度的分隔符,還可以更好地控制識別分隔符的方式。請注意,點字符必須在正則表達式規範中轉義,因爲在正則表達式語言中,「。」充當通配符。

std::vector<std::string> SplitLine(std::string const& line, std::string seps) 
{ 
    std::regex regxSeps(seps); // the dot character needs to be escaped in a regex 
    std::sregex_token_iterator rit(line.begin(), line.end(), regxSeps, -1); 
    return std::vector<std::string>(rit, std::sregex_token_iterator()); 
} 

static int ParseLine(std::string line, std::string seps, size_t startIdx, std::vector<CNode>& collection) 
{ 
    if (startIdx > collection.size()) 
    { 
     throw std::invalid_argument("the start index is out of range"); 
    } 

    auto objectTypes = SplitLine(line, seps); 

    auto idx = startIdx; 
    for (const auto& objectType : objectTypes) 
    { 
     if (idx == collection.size()) 
     { 
      collection.push_back(CNode(idx)); 
     } 
     collection[idx++].SetObjectType(objectType); 
    } 
    return (idx - 1); 
} 

int main() 
{ 
    std::string seps = "\\."; // the dot character needs to be escaped in a regex 
    ... 
} 
+0

謝謝,你使用正則表達式的解決方案很簡潔,並且演示瞭如何用標準庫替換strtok的一個清晰實用的例子。 –

0

解決方案下面使用標準庫(使用得到的回答)取代的strtok這個Utility類。我用這個處理字符串轉換,從,各種方法去除空白,分裂,變化情況等。這裏是一個函數從這個類拆分的字符串:

Utility.h

class Utility { 
public: 
    static std::vector<std::string> splitString(const std::string& strStringToSplit, 
               const std::string& strDelimiter, 
               const bool keepEmpty = true); 

private: 
    Utility(); 
}; 

Utility.cpp

#include "Utility.h" 

// splitString() 
std::vector<std::string> Utility::splitString(const std::string& strStringToSplit, 
               const std::string& strDelimiter, 
               const bool keepEmpty) { 
    std::vector<std::string> vResult; 
    if (strDelimiter.empty()) { 
     vResult.push_back(strStringToSplit); 
     return vResult; 
    } 

    std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd; 
    while (true) { 
     itSubStrEnd = search(itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end()); 
     std::string strTemp(itSubStrStart, itSubStrEnd); 
     if (keepEmpty || !strTemp.empty()) { 
      vResult.push_back(strTemp); 
     } 

     if (itSubStrEnd == strStringToSplit.end()) { 
      break; 
     } 

     itSubStrStart = itSubStrEnd + strDelimiter.size(); 
    } 

    return vResult; 

} // splitString 

所需庫包括用於此實用方法工作是<vector><string>和0幾乎所有的應用程序都大多使用這種軟件。

要使用此功能,我們可以做一個簡單的測試,因爲這:

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

#include "Utility.h" 

int main() { 
    std::string someLongString2("Hello World How Are You"); 

    std::vector<std::string> singleWords; 
    singleWords = Utility::splitString(someLongString, " "); 

    // Space is the delimiter and now each individual word 
    // from the long string are now each a new string stored 
    // in this vector. You can use any character for your delimiter. 
    // Also this function is not limited to having a single character 
    // as its delimiter. You can use a series of characters or specific 
    // words as your delimiter. Such as a comma followed by a space. 

     std::string someLongString2("Hello, World, How, Are, You"); 
     singleWords.clear(); 
     singleWords = Utility::splitString(someLongString2, ", "); 

    return 0; 
} // main 
+0

是否有人知道通過調用標準庫來替換循環的方法?我聽說大多數時候,我們應該通過調用std替換循環。 –

1

我有什麼,但靜態方法,你不能創建實例的實用工具類:

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

std::vector<std::string> GetlineSplit(std::string const& line) { 
    static const char sep = '.'; 
    std::istringstream liness{line}; 
    std::vector<std::string> fields; 
    for(std::string field; std::getline(liness, field, sep);) { 
     fields.push_back(field); 
    } 
    return fields; 
} 

std::vector<std::string> RegexSplit(std::string const& line) { 
    std::regex seps("\\."); // the dot character needs to be escaped in a regex 
    std::sregex_token_iterator rit(line.begin(), line.end(), seps, -1); 
    return std::vector<std::string>(rit, std::sregex_token_iterator()); 
} 

int main() { 
    std::string line = "abc.def.ghi.klm.nop.qrs.tuv.wxyz"; 

    std::cout << "getline split result:\n"; 
    auto fields_getline = GetlineSplit(line); 
    for(const auto& field : fields_getline) { 
     std::cout << field << '\n'; 
    } 

    std::cout << "\nregex split result:\n"; 
    auto fields_regex = RegexSplit(line); 
    for(const auto& field : fields_regex) { 
     std::cout << field << '\n'; 
    } 
} 
+1

我喜歡你的Utility :: splitString()方法。非常感謝。 –

+0

@LessWhite非常感謝你;我在技術上沒有寫出來;但是在另一年前,當我使用VS2010或者12版時,它已經顯示給我了,並且它一直在我的集合中,並且我始終使用這個靜態類:因此可以重用:)。我在這個實用程序中有大約10到20種方法可以派上用場,而不必一遍又一遍地寫下來。有些是自己寫的,有些是別人寫的。如果我能記得誰給我看了這個功能;我會給他們這個榮譽,但我仍然希望在他們需要的時候幫助別人。 –