2017-05-12 11 views
5

का उपयोग कर स्ट्रोक के साथ एक लूप को बदलें मुझे एक समस्या है जो स्ट्रेटोक हल करता है (एक स्ट्रिंग से विभाजित सबस्ट्रिंग्स) लेकिन मैं महसूस करता हूं कि स्ट्रोक सुरक्षित नहीं है। मैं सी ++ मानक पुस्तकालय के कुछ और आधुनिक भागों का उपयोग करना चाहता हूं।मानक लाइब्रेरी

इसके बजाय मुझे क्या उपयोग करना चाहिए?

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

धन्यवाद, रेगेक्स का उपयोग करके आपका समाधान संक्षेप में है और मानक लाइब्रेरी के साथ स्ट्रोक को प्रतिस्थापित करने का एक स्पष्ट और व्यावहारिक मामला दिखाता है। –

0

समाधान नीचे मानक पुस्तकालय का उपयोग कर (उत्तर प्राप्त का उपयोग करके) strtok को बदलने के लिए यह उपयोगिता वर्ग। मैं एक समारोह सफेद स्थान, बंटवारे को दूर करने के लिए, परिवर्तित करने से तार के साथ काम करने के विभिन्न तरीके के लिए इसका उपयोग, मामलों को बदलने आदि यहाँ है इस वर्ग से एक स्ट्रिंग विभाजित करने के लिए:

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> औरजो आमतौर पर लगभग सभी अनुप्रयोगों में उपयोग किया जाता है।

इस सुविधा का उपयोग करने के लिए हम इस के रूप में एक साधारण परीक्षण कर सकते हैं:

#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

क्या कोई भी मानक पुस्तकालय में कॉल द्वारा लूप को प्रतिस्थापित करने का तरीका जानता है? मैंने सुना है कि ज्यादातर समय, हमें lod को 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

मुझे आपकी उपयोगिता :: splitString() विधि पसंद है। बहुत बहुत धन्यवाद। –

+0

@LessWhite आपको बहुत बहुत धन्यवाद; मैंने तकनीकी रूप से इसे नहीं लिखा; लेकिन यह मुझे एक और साल पहले दिखाया गया था जब मैं वीएस -2010 या 12 का उपयोग कर रहा था और यह तब से मेरे संग्रह में रहा है और मैं हर समय इस स्थिर वर्ग का उपयोग करता हूं: इसलिए पुन: प्रयोज्य :)। मेरे पास इस उपयोगिता वर्ग में लगभग 10-20 विधियां हैं जो उन्हें बार-बार लिखने के बिना काम में आती हैं। कुछ मेरे द्वारा लिखे गए थे जबकि कुछ दूसरों द्वारा लिखे गए थे।अगर मैं यह याद रखने में सक्षम था कि मुझे यह कार्य किसने दिखाया; मैं उन्हें इसके लिए श्रेय दूंगा लेकिन मैं अभी भी ज़रूरत के समय में दूसरों की मदद करना चाहता हूं। –

संबंधित मुद्दे