स्ट्रिंग के वेक्टर को एक स्ट्रिंग में कैसे फंसाना है (सुरुचिपूर्ण तरीके से)


83

मैं एक स्ट्रिंग में स्ट्रिंग के एक वेक्टर को फंसाने के लिए सबसे सुंदर तरीके की तलाश कर रहा हूं। नीचे अब मैं उपयोग कर रहा हूँ समाधान है:

static std::string& implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
    for (std::vector<std::string>::const_iterator ii = elems.begin(); ii != elems.end(); ++ii)
    {
        s += (*ii);
        if ( ii + 1 != elems.end() ) {
            s += delim;
        }
    }

    return s;
}

static std::string implode(const std::vector<std::string>& elems, char delim)
{
    std::string s;
    return implode(elems, delim, s);
}

क्या कोई अन्य व्यक्ति वहाँ है?


आप इस फ़ंक्शन को क्यों कहते हैं?
कर्नल पैनिक

5
@ColonelPanic, PHP के निहित () विधि के साथ सादृश्य द्वारा, जो सरणी तत्वों को जोड़ता है और उन्हें एकल स्ट्रिंग के रूप में आउटपुट करता है। मुझे आश्चर्य है कि आप यह सवाल क्यों पूछ रहे हैं :)
ezpresso

जवाबों:


133

उपयोग करें boost::algorithm::join(..):

#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);

यह प्रश्न भी देखें ।


57
सरल स्ट्रिंग बनाने के लिए बड़े पैमाने पर बढ़ावा देने वाली लाइब्रेरी के खिलाफ शामिल करने और लिंक करने का सुझाव देना बेतुका है।
जूलियन

8
@ जूलियन ज्यादातर प्रोजेक्ट पहले से ही ऐसा करते हैं। मैं मानता हूं कि यह बेतुका है कि एसटीएल में पहले से ही ऐसा करने का तरीका शामिल नहीं है। मैं यह भी मान सकता हूं कि यह शीर्ष उत्तर नहीं होना चाहिए , लेकिन अन्य उत्तर स्पष्ट रूप से उपलब्ध हैं।
नदी टैम

मैं @ जूलियन के साथ कॉनक्योर करता हूं। बूस्ट उपयोग में सुरुचिपूर्ण हो सकता है लेकिन ओवरहेड के संदर्भ में "सबसे सुरुचिपूर्ण तरीका" नहीं है। इस मामले में, यह ओपी के एल्गोरिथ्म के लिए एक समाधान है और प्रश्न का समाधान नहीं है।
एज़रोथ 2 बी

3
अधिकांश बूस्ट लाइब्रेरी हेडर-ओनली हैं, इसलिए लिंक करने के लिए कुछ भी नहीं है। कुछ भी मानक में अपना रास्ता बनाते हैं।
जाबुनि

28
std::vector<std::string> strings;

const char* const delim = ", ";

std::ostringstream imploded;
std::copy(strings.begin(), strings.end(),
           std::ostream_iterator<std::string>(imploded, delim));

(शामिल <string>, <vector>, <sstream>और <iterator>)

यदि आप एक साफ अंत चाहते हैं (कोई अनुगामी सीमांकक) यहाँ एक नज़र है


9
हालांकि, यह ध्यान रखें कि यह अतिरिक्त सीमांकक ( std::ostream_iteratorस्ट्रीम के अंत में कंस्ट्रक्टर के लिए दूसरा पैरामीटर जोड़ देगा ।)
माइकल क्रेलिन - हैकर

9
"प्रत्यारोपण" की बात यह है कि एक सीमांकक अंतिम नहीं जोड़ा जाना चाहिए। यह उत्तर दुर्भाग्य से उस परिसीमन को अंतिम रूप से जोड़ता है।
जॉनी

और सौभाग्य से मुझे टोकन को अंतिम रूप से जोड़ना होगा! समाधान के लिए धन्यवाद।
Константин Ван

20

आपको आउटपुट बनाने के std::ostringstreamबजाय उपयोग करना चाहिए std::string(तब आप str()स्ट्रिंग प्राप्त करने के लिए अंत में इसकी विधि को कॉल कर सकते हैं , इसलिए आपके इंटरफ़ेस को बदलने की आवश्यकता नहीं है, केवल अस्थायी है s)।

वहां से, आप उपयोग करने के लिए बदल सकते हैं std::ostream_iterator, जैसे:

copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim)); 

लेकिन इसकी दो समस्याएं हैं:

  1. delimअब एक const char*के बजाय एक होने की जरूरत है char। कोई बड़ी बात नहीं।
  2. std::ostream_iteratorअंतिम सहित हर एक तत्व के बाद परिसीमन लिखता है। तो आपको या तो अंतिम छोर को मिटाना होगा, या अपने स्वयं के संस्करण को लिखना होगा जिसमें यह झुंझलाहट नहीं है। यदि आपके पास इस तरह की चीजों की आवश्यकता है, तो आपके पास बहुत से कोड होने चाहिए; अन्यथा पूरी गड़बड़ी से बचा जा सकता है (यानी उपयोग करें ostringstreamलेकिन नहीं ostream_iterator)।

1
या एक का उपयोग करें जो पहले से लिखा है: stackoverflow.com/questions/3496982/…
जैरी कॉफिन

13

क्योंकि मुझे वन-लाइनर्स से प्यार है (वे सभी प्रकार के अजीब सामानों के लिए बहुत उपयोगी हैं, जैसा कि आप अंत में देखेंगे), यहाँ std का उपयोग कर एक समाधान है :: संचय और C ++ 11 लैंबडा:

std::accumulate(alist.begin(), alist.end(), std::string(), 
    [](const std::string& a, const std::string& b) -> std::string { 
        return a + (a.length() > 0 ? "," : "") + b; 
    } )

मैं इस सिंटैक्स को स्ट्रीम ऑपरेटर के साथ उपयोगी मानता हूं, जहां मैं स्ट्रीम ऑपरेशन से सभी प्रकार के अजीब तर्क को दायरे से बाहर नहीं करना चाहता, बस एक साधारण स्ट्रिंग में शामिल होने के लिए। उदाहरण के लिए इस रिटर्न स्टेटमेंट पर विचार करें कि स्ट्रीम ऑपरेटर्स (std का उपयोग करके स्ट्रिंग);

return (dynamic_cast<ostringstream&>(ostringstream()
    << "List content: " << endl
    << std::accumulate(alist.begin(), alist.end(), std::string(), 
        [](const std::string& a, const std::string& b) -> std::string { 
            return a + (a.length() > 0 ? "," : "") + b; 
        } ) << endl
    << "Maybe some more stuff" << endl
    )).str();

अपडेट करें:

जैसा कि @plexando द्वारा टिप्पणियों में बताया गया है, उपरोक्त कोड दुर्व्यवहार से ग्रस्त है जब सरणी इस तथ्य के कारण खाली तारों से शुरू होती है कि "पहले रन" के लिए चेक पिछले रन से गायब है, जिसके परिणामस्वरूप कोई अतिरिक्त वर्ण नहीं है, और यह भी। सभी रन पर "पहले रन" के लिए एक चेक चलाना अजीब है (यानी कोड अंडर-अनुकूलित है)।

इन दोनों समस्याओं का समाधान आसान है यदि हम इस तथ्य के लिए जानते हैं कि सूची में कम से कम एक तत्व है। OTOH, अगर हम एक तथ्य यह है कि सूची के लिए पता नहीं है कम से कम एक तत्व है, तो हम रन और भी छोटा कर सकते हैं।

मुझे लगता है कि परिणामी कोड बहुत सुंदर नहीं है, इसलिए मैं इसे यहां सही समाधान के रूप में जोड़ रहा हूं , लेकिन मुझे लगता है कि ऊपर चर्चा अभी भी पूरी तरह से है:

alist.empty() ? "" : /* leave early if there are no items in the list
  std::accumulate( /* otherwise, accumulate */
    ++alist.begin(), alist.end(), /* the range 2nd to after-last */
    *alist.begin(), /* and start accumulating with the first item */
    [](auto& a, auto& b) { return a + "," + b; });

टिप्पणियाँ:

  • पहले तत्व तक सीधी पहुंच का समर्थन करने वाले कंटेनरों के लिए, संभवतः तीसरे तर्कों के लिए इसका उपयोग करना बेहतर है, इसलिए alist[0]वैक्टर के लिए।
  • टिप्पणियों और चैट में चर्चा के अनुसार, लैम्बडा अभी भी कुछ कॉपी करता है। इसके बजाय (कम सुंदर) लंबोदा का उपयोग करके इसे कम किया जा सकता है: [](auto&& a, auto&& b) -> auto& { a += ','; a += b; return a; })जो (जीसीसी 10 पर) x10 से अधिक के प्रदर्शन में सुधार करता है। सुझाव के लिए @Deduplicator का धन्यवाद। मैं अभी भी यह पता लगाने की कोशिश कर रहा हूं कि यहां क्या हो रहा है।

4
accumulateतार के लिए उपयोग न करें । अधिकांश अन्य उत्तर O (n) हैं, लेकिन accumulateO (n ^ 2) है क्योंकि यह अपने तत्व को जोड़ने से पहले संचायक की एक अस्थायी प्रतिलिपि बनाता है। और नहीं, चाल शब्दार्थ मदद नहीं करते।
ओकटालिस्ट

2
@Oktalist, मुझे यकीन नहीं है कि आप ऐसा क्यों कहते हैं - cplusplus.com/reference/numeric/accumulate कहते हैं "जटिलता पहले और आखिरी के बीच की दूरी में रैखिक है"।
गस

1
यह मानते हुए कि प्रत्येक व्यक्तिगत जोड़ को निरंतर समय लगता है। यदि Tकोई अतिभारित है operator+(जैसे stringकरता है) या यदि आप अपने स्वयं के फ़नकार प्रदान करते हैं तो सभी दांव बंद हैं। हालांकि मैं यह कहने में जल्दबाजी कर सकता हूं कि चाल शब्दार्थवाद मदद नहीं करता है, उन्होंने दो कार्यान्वयनों में समस्या को हल नहीं किया है जिन्हें मैंने जांचा है। इसी तरह के सवालों के मेरे जवाब देखें ।
ओकटालिस्ट

1
skwllsp की टिप्पणी का इससे कोई लेना-देना नहीं है। जैसा मैंने कहा, अन्य अधिकांश उत्तर (और ओपी का implodeउदाहरण) सही काम कर रहे हैं। वे O (n) हैं, भले ही वे reserveस्ट्रिंग पर कॉल न करें । केवल संचय का उपयोग कर समाधान O (n ^ 2) है। सी-स्टाइल कोड की कोई आवश्यकता नहीं।
Oktalist

12
मैंने एक बेंचमार्क किया , और संचय वास्तव में एक ओ (एन) स्ट्रिंग स्ट्रीम की तुलना में तेज था।
kirbyfan64sos

11

सरल बेवकूफ समाधान के बारे में क्या?

std::string String::join(const std::vector<std::string> &lst, const std::string &delim)
{
    std::string ret;
    for(const auto &s : lst) {
        if(!ret.empty())
            ret += delim;
        ret += s;
    }
    return ret;
}



6

विशेष रूप से बड़े संग्रहों के साथ, आप जाँच करना चाहते हैं कि क्या आप अभी भी पहले तत्व को जोड़ रहे हैं या नहीं कि कोई अनुगामी विभाजक सुनिश्चित न हो ...

तो खाली या एकल-तत्व सूची के लिए, कोई पुनरावृत्ति बिल्कुल नहीं है।

खाली श्रेणियां तुच्छ हैं: "" लौटें।

एकल तत्व या बहु-तत्व को पूरी तरह से नियंत्रित किया जा सकता है accumulate:

auto join = [](const auto &&range, const auto separator) {
    if (range.empty()) return std::string();

    return std::accumulate(
         next(begin(range)), // there is at least 1 element, so OK.
         end(range),

         range[0], // the initial value

         [&separator](auto result, const auto &value) {
             return result + separator + value;
         });
};

रनिंग सैंपल ( C ++ 14 की आवश्यकता है ): http://cpp.sh/8uspd


3

एक संस्करण जो उपयोग करता है std::accumulate:

#include <numeric>
#include <iostream>
#include <string>

struct infix {
  std::string sep;
  infix(const std::string& sep) : sep(sep) {}
  std::string operator()(const std::string& lhs, const std::string& rhs) {
    std::string rz(lhs);
    if(!lhs.empty() && !rhs.empty())
      rz += sep;
    rz += rhs;
    return rz;
  }
};

int main() {
  std::string a[] = { "Hello", "World", "is", "a", "program" };
  std::string sum = std::accumulate(a, a+5, std::string(), infix(", "));
  std::cout << sum << "\n";
}

2

यहाँ एक और है जो अंतिम तत्व के बाद सीमांकक नहीं जोड़ता है:

std::string concat_strings(const std::vector<std::string> &elements,
                           const std::string &separator)
{       
    if (!elements.empty())
    {
        std::stringstream ss;
        auto it = elements.cbegin();
        while (true)
        {
            ss << *it++;
            if (it != elements.cend())
                ss << separator;
            else
                return ss.str();
        }       
    }
    return "";

2

एक अन्य प्रश्न के उत्तर के भाग का उपयोग करना , आपको एक सम्मिलित अल्पविराम के बिना विभाजक के आधार पर, इसमें शामिल होने देता है,

उपयोग:

std::vector<std::string> input_str = std::vector<std::string>({"a", "b", "c"});
std::string result = string_join(input_str, ",");
printf("%s", result.c_str());
/// a,b,c

कोड:

std::string string_join(const std::vector<std::string>& elements, const char* const separator)
{
    switch (elements.size())
    {
        case 0:
            return "";
        case 1:
            return elements[0];
        default:
            std::ostringstream os;
            std::copy(elements.begin(), elements.end() - 1, std::ostream_iterator<std::string>(os, separator));
            os << *elements.rbegin();
            return os.str();
    }
}

1

यहाँ मैं क्या उपयोग कर रहा हूँ, सरल और लचीला

string joinList(vector<string> arr, string delimiter)
{
    if (arr.empty()) return "";

    string str;
    for (auto i : arr)
        str += i + delimiter;
    str = str.substr(0, str.size() - delimiter.size());
    return str;
}

का उपयोग कर:

string a = joinList({ "a", "bbb", "c" }, "!@#");

उत्पादन:

a!@#bbb!@#c

0

थोड़ा लंबा समाधान, लेकिन उपयोग नहीं करता है std::ostringstream, और अंतिम सीमांकक को हटाने के लिए हैक की आवश्यकता नहीं है।

http://www.ideone.com/hW1M9

और कोड:

struct appender
{
  appender(char d, std::string& sd, int ic) : delim(d), dest(sd), count(ic)
  {
    dest.reserve(2048);
  }

  void operator()(std::string const& copy)
  {
    dest.append(copy);
    if (--count)
      dest.append(1, delim);
  }

  char delim;
  mutable std::string& dest;
  mutable int count;
};

void implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
  std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size()));
}

0

टर्नरी ऑपरेटर के साथ एक संभावित समाधान ?:

std::string join(const std::vector<std::string> & v, const std::string & delimiter = ", ") {
    std::string result;

    for (size_t i = 0; i < v.size(); ++i) {
        result += (i ? delimiter : "") + v[i]; 
    }

    return result;
}

join({"2", "4", "5"})आपको दे देंगे 2, 4, 5


0

Fmt से आप कर सकते हैं।

#include <fmt/format.h>
auto s = fmt::format("{}",fmt::join(elems,delim)); 

लेकिन मुझे नहीं पता कि ज्वाइन करने से यह std :: फॉर्मेट में आएगा।



-1

यह कोशिश करो, लेकिन सूची के बजाय वेक्टर का उपयोग करना

template <class T>
std::string listToString(std::list<T> l){
    std::stringstream ss;
    for(std::list<int>::iterator it = l.begin(); it!=l.end(); ++it){
        ss << *it;
        if(std::distance(it,l.end())>1)
            ss << ", ";
    }
    return "[" + ss.str()+ "]";
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.