2012-02-14 22 views
16

मैं एक कस्टम डेलीमीटर के साथ vector की सामग्री को एक लंबे string पर प्रतिलिपि बनाना चाहता हूं। अब तक, मैं कोशिश की है:std :: वेक्टर स्ट्रिंग करने के लिए

// .h 
string getLabeledPointsString(const string delimiter=","); 
// .cpp 
string Gesture::getLabeledPointsString(const string delimiter) { 
    vector<int> x = getLabeledPoints(); 
    stringstream s; 
    copy(x.begin(),x.end(), ostream_iterator<int>(s,delimiter)); 
    return s.str(); 
} 

लेकिन मैं

no matching function for call to ‘std::ostream_iterator<int, char, std::char_traits<char> >::ostream_iterator(std::stringstream&, const std::string&)’ 

मैं charT* साथ की कोशिश की है, लेकिन मैं मिल

error iso c++ forbids declaration of charT with no type 

फिर मिल रहा char और ostream_iterator<int>(s,&delimiter) लेकिन का उपयोग कर की कोशिश की मुझे स्ट्रिंग में अजीब अक्षर मिलते हैं।

क्या कोई मुझे समझने में मदद कर सकता है कि संकलक यहां क्या अपेक्षा कर रहा है?

+0

हाँ यह अच्छा नहीं होगा अगर संकलक ने आपको बताया कि यह किस प्रकार की उम्मीद कर रहा था। संयोग से आप अपने अंतिम तत्व के बाद भी एक अल्पविराम प्राप्त करेंगे। – CashCow

+0

सबसे शानदार तरीका यह है कि http://stackoverflow.com/a/6334153/2056686 – StefanQ

उत्तर

19

Use delimiter.c_str() as the delimiter:

copy(x.begin(),x.end(), ostream_iterator<int>(s,delimiter.c_str())); 

इस तरह, आप स्ट्रिंग है, जो है क्या ostream_operator अपने std::string से उम्मीद की ओर इशारा करते एक const char* मिलता है।

+15

+1 में वर्णित के रूप में boost :: एल्गोरिदम :: join() का उपयोग करना है, लेकिन यह भी ध्यान दें कि यह एक पिछला 'डेलीमीटर आउटपुट में। –

+1

मुझे यहां प्रदर्शन के बारे में निश्चित नहीं है। एक 'स्ट्रिंगस्ट्रीम' अपने स्वयं के बफर का प्रबंधन करता है, इसलिए इसे गतिशील रूप से बढ़ना चाहिए। यहां स्ट्रिंग उत्पन्न करने से पहले आप जानते हैं कि यह कितनी लंबाई होगी, इसलिए आपको बंजर को संगत करने से पहले आरक्षित करना चाहिए। – wilhelmtell

+1

"स्ट्रिंग उत्पन्न करने से पहले आप जानते हैं कि यह कितनी लंबाई होगी" - या किसी भी दर पर आप ऊपरी बाउंड को जानते हैं। यदि स्ट्रिंगस्ट्रीम तेजी से अपने बफर को बढ़ाता है तो मैं प्रदर्शन के बारे में चिंता नहीं करता, लेकिन मुझे नहीं पता कि यह मामला है या नहीं। –

6
std::string Gesture::getLabeledPointsString(const std::string delimiter) { 
    return boost::join(getLabeledPoints(), delimiter); 
} 

मुझे लगता है कि इस बिंदु पर getLabeledPointsString introducting के बारे में आश्वस्त नहीं हूँ;)

+1

ईहेहेहेहे बिना किसी बूस्ट के सीपीपी है? बढ़ावा देने के लिए +1, धन्यवाद! – nkint

+0

@ एनकिंट: ओह आप निश्चित रूप से बिना किसी बढ़ावा के प्रोग्राम कर सकते हैं। लेकिन यह पाइथन के पुस्तकालयों के बिना जितना मुश्किल है: आपको बस अपने सभी टूल बनाने की ज़रूरत है;) –

8

एक और तरीका है यह करने के लिए:

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

template <typename T> 
string join(const T& v, const string& delim) { 
    ostringstream s; 
    for (const auto& i : v) { 
     if (&i != &v[0]) { 
      s << delim; 
     } 
     s << i; 
    } 
    return s.str(); 
} 

int main() { 
    cout << join(vector<int>({1, 2, 3, 4, 5}), ",") << endl; 
} 

(C++ 11 पाश और 'ऑटो के लिए सीमा के आधार पर 'हालांकि)

7

सी ++ 11:

vector<string> x = {"1", "2", "3"}; 
string s = std::accumulate(std::begin(x), std::end(x), string(), 
           [](string &ss, string &s) 
           { 
            return ss.empty() ? s : ss + "," + s; 
           }); 
+1

साफ दिखता है, लेकिन क्या यह प्रक्रिया में कई स्ट्रिंग नहीं बनाएगा? स्ट्रिंग स्ट्रीम का उपयोग करके इसे सुधारने का कोई तरीका? – oferei

+0

नहीं, sttringsream धीमा है और भी अधिक है। वास्तव में यदि आपके पास तारों का केवल कंटेनर है तो 'कोड' स्ट्रिंग परिणाम के लिए सामान्य लूप लिखना बेहतर है; परिणाम.reserve (128); (ऑटो और इसे: एक्स) { अगर (! Result.emty()) { परिणाम.एपेंड (","); } परिणाम.एपेंड (इसे); } 'कोड' –

+0

उचित लगता है, लेकिन यह उपर्युक्त कोड नहीं है। यह संलग्न नहीं है - यह नए तार पैदा कर रहा है। – oferei

-1

तेजी संस्करण:

vector<string> x = {"1", "2", "3"}; 
string res; 
res.reserve(16); 

std::accumulate(std::begin(x), std::end(x), 0, 
       [&res](int &, string &s) 
       { 
        if (!res.empty()) 
        { 
         res.append(","); 
        } 
        res.append(s); 
        return 0; 
       }); 

यह अंतरिम तार बनाने नहीं है, लेकिन सिर्फ पूरी स्ट्रिंग परिणाम के लिए एक बार स्मृति को आबंटित और & रेस

+0

आप 'जमा करें') का उपयोग कर रहे हैं लेकिन परिणाम को पूरी तरह से अनदेखा करते हैं। आपको इसके बजाय 'std :: for_each()' का उपयोग करना चाहिए। –

+0

वास्तव में इस फ़ंक्शन कॉल का मूल्य वापस 0 है (देखें lambda -> वापसी 0;)। लेकिन 'res' में परिणाम स्ट्रिंग –

+0

है क्या आप जानते हैं कि 'संचय' की अपेक्षा है कि इसके फ़ंक्शन के दुष्प्रभाव नहीं हैं?यह कोड बेहतर 'for_each' के रूप में व्यक्त किया जाएगा। – xtofl

2
int array[ 6 ] = { 1, 2, 3, 4, 5, 6 }; 
std::vector<int> a(array, array + 6); 
stringstream dataString; 
ostream_iterator<int> output_iterator(dataString, ";"); // here ";" is delimiter 
std::copy(a.begin(), a.end(), output_iterator); 
cout<<dataString.str()<<endl; 

उत्पादन = 1 के अंत करने के लिए प्रत्येक ELEM संलग्न कर देता है , 2, 3, 4, 5, 6;

+1

आम तौर पर, उत्तर अधिक उपयोगी होते हैं यदि उनमें कोड का क्या इरादा है, और यह दूसरों को पेश किए बिना समस्या का हल क्यों करता है, इसका स्पष्टीकरण शामिल है। –

+0

यह काम किया! छोटा एवं सुन्दर। धन्यवाद। – Amjay

3

यह पहले से ऊपर दिए गए दो उत्तरों का विस्तार है क्योंकि रन-टाइम प्रदर्शन टिप्पणियों में एक विषय प्रतीत होता है। मैंने इसे टिप्पणियों के रूप में जोड़ा होगा, लेकिन मेरे पास अभी तक यह विशेषाधिकार नहीं है।

मैं दृश्य स्टूडियो 2015 का उपयोग कर चलाने के समय प्रदर्शन के लिए 2 कार्यान्वयन का परीक्षण किया:

का उपयोग stringstream:

std::stringstream result; 
auto it = vec.begin(); 
result << (unsigned short)*it++; 
for (; it != vec.end(); it++) { 
    result << delimiter; 
    result << (unsigned short)*it; 
} 
return result.str(); 

का उपयोग करते हुए जमा:

std::string result = std::accumulate(std::next(vec.begin()), vec.end(), 
    std::to_string(vec[0]), 
    [&delimiter](std::string& a, uint8_t b) { 
    return a + delimiter+ std::to_string(b); 
}); 
return result; 

रिलीज निर्माण रन-टाइम प्रदर्शन करीब था कुछ subtleties के साथ।

एकत्रित कार्यान्वयन 256 तत्व वेक्टर पर 1000 पुनरावृत्तियों पर कुल रन-टाइम (~ 180ms) का 20-50ms, ~ 10-30% था()।हालांकि, accumulate कार्यान्वयन केवल तभी तेज था जब लैम्ब्डा फ़ंक्शन के a पैरामीटर संदर्भ द्वारा पारित किया गया था। मूल्य के अनुसार a पैरामीटर को पास करने के परिणामस्वरूप stringstream कार्यान्वयन के पक्ष में समान रन-टाइम अंतर होता है। accumulate कार्यान्वयन ने कुछ सुधार भी किया जब परिणाम स्ट्रिंग को स्थानीय वैरिएबल को असाइन किए जाने के बजाय सीधे वापस कर दिया गया था जिसे तुरंत वापस कर दिया गया था। अन्य सी ++ कंपाइलर्स के साथ वाईएमएमवी।

डीबग बिल्ड accumulate का उपयोग करके 5-10 गुना धीमा था, इसलिए मुझे लगता है कि उपरोक्त कई टिप्पणियों में उल्लिखित अतिरिक्त स्ट्रिंग निर्माण ऑप्टिमाइज़र द्वारा हल किया गया है।

मैं uint8_t मानों के vector का उपयोग करके एक विशिष्ट कार्यान्वयन को देख रहा था। पूर्ण परीक्षण कोड इस प्रकार है:

#include <vector> 
#include <iostream> 
#include <sstream> 
#include <numeric> 
#include <chrono> 

using namespace std; 
typedef vector<uint8_t> uint8_vec_t; 

string concat_stream(const uint8_vec_t& vec, string& delim = string(" ")); 
string concat_accumulate(const uint8_vec_t& vec, string& delim = string(" ")); 

string concat_stream(const uint8_vec_t& vec, string& delimiter) 
{ 
    stringstream result; 

    auto it = vec.begin(); 
    result << (unsigned short)*it++; 
    for (; it != vec.end(); it++) { 
     result << delimiter; 
     result << (unsigned short)*it; 
    } 
    return result.str(); 
} 

string concat_accumulate(const uint8_vec_t& vec, string& delimiter) 
{ 
    return accumulate(next(vec.begin()), vec.end(), 
     to_string(vec[0]), 
     [&delimiter](string& a, uint8_t b) { 
     return a + delimiter + to_string(b); 
    }); 
} 

int main() 
{ 
    const int elements(256); 
    const int iterations(1000); 

    uint8_vec_t test(elements); 
    iota(test.begin(), test.end(), 0); 

    int i; 
    auto stream_start = chrono::steady_clock::now(); 
    string join_with_stream; 
    for (i = 0; i < iterations; ++i) { 
     join_with_stream = concat_stream(test); 
    } 
    auto stream_end = chrono::steady_clock::now(); 

    auto acc_start = chrono::steady_clock::now(); 
    string join_with_acc; 
    for (i = 0; i < iterations; ++i) { 
     join_with_acc = concat_accumulate(test); 
    } 
    auto acc_end = chrono::steady_clock::now(); 

    cout << "Stream Results:" << endl; 
    cout << " elements: " << elements << endl; 
    cout << " iterations: " << iterations << endl; 
    cout << " runtime: " << chrono::duration<double, milli>(stream_end - stream_start).count() << " ms" << endl; 
    cout << " result: " << join_with_stream << endl; 

    cout << "Accumulate Results:" << endl; 
    cout << " elements: " << elements << endl; 
    cout << " iterations: " << iterations << endl; 
    cout << " runtime: " << chrono::duration<double, milli>(acc_end - acc_start).count() << " ms" << endl; 
    cout << " result:" << join_with_acc << endl; 

    return 0; 
} 
+0

मुझे प्यार है कि आपने प्रदर्शन संख्या प्रदान की - एक निर्णय लेने के लिए बहुत उपयोगी या दूसरा, धन्यवाद! – cyberbisson

1
string join(const vector<string> & v, const string & delimiter = ",") { 
    string out; 
    if (auto i = v.begin(), e = v.end(); i != e) { 
     out += *i++; 
     for (; i != e; ++i) out.append(delimiter).append(*i); 
    } 
    return out; 
} 

कुछ अंक:

  • क्या आप वाकई दुर्घटना नहीं है एक अतिरिक्त अनुगामी सीमांकक
  • बनाने से बचने के लिए एक अतिरिक्त सशर्त की जरूरत नहीं है जब वेक्टर खाली होता है
  • अस्थायी समूह का समूह न बनाएं (उदाहरण के लिए ऐसा न करें: x = x + d + y)
संबंधित मुद्दे