แปลงเวกเตอร์ <int> เป็นสตริง


93

ฉันมีvector<int>คอนเทนเนอร์ที่มีจำนวนเต็ม (เช่น {1,2,3,4}) และฉันต้องการแปลงเป็นสตริงของแบบฟอร์ม

"1,2,3,4"

วิธีที่สะอาดที่สุดใน C ++ คืออะไร? ใน Python นี่คือวิธีที่ฉันจะทำ:

>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'

1
ที่เกี่ยวข้องอย่างใกล้ชิด: stackoverflow.com/questions/4850473/…
Ciro Santilli 郝海东冠状病六四事件法轮功

คำตอบ:


96

แน่นอนว่าไม่หรูหราเท่า Python แต่ไม่มีอะไรที่สง่างามเท่ากับ Python ใน C ++

คุณสามารถใช้stringstream...

#include <sstream>
//...

std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
  if(i != 0)
    ss << ",";
  ss << v[i];
}
std::string s = ss.str();

คุณยังสามารถใช้std::for_eachแทน


ฉันคิดว่าคุณหมายถึง array.size () ไม่ใช่ v.size () ไม่ใช่เหรอ?
Mark Elliot

1
อะไรก็ตามที่เรียกว่าเวกเตอร์
Brian R.Bondy

2
std::string s = ss.str()ที่ควรจะเป็น หากคุณต้องการconst char*ใช้s.c_str(). (โปรดทราบว่าในขณะที่ถูกต้องตามไวยากรณ์ss.str().c_str()จะทำให้คุณมีconst char*จุดที่ชี้ไปที่ชั่วคราวซึ่งจะหยุดอยู่ในตอนท้ายของนิพจน์ทั้งหมดนั่นเจ็บ)
sbi

1
ทำไมไม่ใช้แค่ string.append?
Baiyan Huang

12
คำตอบไม่สมบูรณ์หากไม่มี#include <sstream>
renadeen

44

การใช้ std :: for_each และ lambda คุณสามารถทำสิ่งที่น่าสนใจได้

#include <iostream>
#include <sstream>

int main()
{
     int  array[] = {1,2,3,4};
     std::for_each(std::begin(array), std::end(array),
                   [&std::cout, sep=' '](int x) mutable {
                       out << sep << x; sep=',';
                   });
}

ดูคำถามนี้สำหรับชั้นเรียนเล็ก ๆ ที่ฉันเขียน การดำเนินการนี้จะไม่พิมพ์เครื่องหมายจุลภาคต่อท้าย นอกจากนี้หากเราคิดว่า C ++ 14 จะยังคงให้อัลกอริทึมเทียบเท่ากับช่วงตามช่วงเช่นนี้:

namespace std {
   // I am assuming something like this in the C++14 standard
   // I have no idea if this is correct but it should be trivial to write if it  does not appear.
   template<typename C, typename I>
   void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;   
int main()
{
     int  array[] = {1,2,3,4};
     std::copy(array, POI(std::cout, ","));
  // ",".join(map(str,array))               // closer
}

12
ฉันคิดว่าสิ่งนี้ไม่เทียบเท่ากับการเข้าร่วมของ Python - มันจะแทรก "," พิเศษไว้ในตอนท้าย
1800 ข้อมูล

2
ไม่เทียบเท่า แต่สวยหรู (อันที่จริงฉันคิดมากกว่านั้น แต่นั่นเป็นเพียงความเห็น)
Martin York

21
เห็นได้ชัดว่าความสง่างามเป็นเรื่องส่วนตัว ดังนั้นหากคุณและอีกสองคนชอบรหัสที่ยาวกว่าและซ้ำซากกว่าซึ่งใช้ไม่ได้ก็จะดูสง่างามมากขึ้น - p
Steve Jessop

1
คุณสามารถละเว้นเครื่องหมายจุลภาคสุดท้ายโดยใช้ฟังก์ชันสมาชิก string :: substr กำหนดสตริงย่อยจาก 0 ถึง n-1 ให้กับตัวแปรผลลัพธ์ของคุณ
แดน

@SteveJessop: ดีกว่ามั้ย?
Martin York

24

คุณสามารถใช้ std :: collect ลองพิจารณาตัวอย่างต่อไปนี้

if (v.empty() 
    return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
                     [](const std::string& a, int b){
                           return a + ',' + std::to_string(b);
                     });

','ควรจะเป็น","
Matt

2
@Matt stringคลาสมีโอเวอร์โหลดสำหรับ+ตัวดำเนินการที่สามารถรับอักขระได้เช่นกัน ดังนั้น','เป็นเพียงแค่ปรับ
Pavan Manjunath

19

อีกทางเลือกหนึ่งคือการใช้std::copyและostream_iteratorคลาส:

#include <iterator>  // ostream_iterator
#include <sstream>   // ostringstream
#include <algorithm> // copy

std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);

ยังไม่ดีเท่า Python เพื่อจุดประสงค์นี้ฉันจึงสร้างjoinฟังก์ชัน:

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  for (A it=begin;
       it!=end;
       it++)
  {
    if (!result.empty())
      result.append(t);
    result.append(*it);
  }
  return result;
}

จากนั้นใช้มันดังนี้:

std::string s=join(array.begin(), array.end(), std::string(","));

คุณอาจถามว่าทำไมฉันถึงผ่านในตัววนซ้ำ อันที่จริงฉันต้องการย้อนกลับอาร์เรย์ดังนั้นฉันจึงใช้มันดังนี้:

std::string s=join(array.rbegin(), array.rend(), std::string(","));

ตามหลักการแล้วฉันต้องการสร้างเทมเพลตจนถึงจุดที่สามารถอนุมานประเภทถ่านและใช้สตริงสตรีมได้ แต่ฉันยังคิดไม่ออก


เนื่องจากมันจะมากเกินไปสำหรับความคิดเห็นฉันจึงโพสต์คำตอบ ( stackoverflow.com/questions/1430757/1432040#1432040 ) ซึ่งพยายามแก้ปริศนาที่ให้ไว้ในประโยคสุดท้ายของคุณ
sbi

joinฟังก์ชันของคุณสามารถใช้กับเวกเตอร์ได้เช่นกัน? โปรดยกตัวอย่างเช่นฉันเพิ่งเริ่มใช้ C ++
Noitidart

คุณสามารถเปลี่ยน iterator เป็น preincrement ในคำตอบได้หรือไม่?
Millie Smith

14

ด้วย Boost และ C ++ 11 สิ่งนี้สามารถทำได้ดังนี้:

auto array = {1,2,3,4};
join(array | transformed(tostr), ",");

เกือบแล้ว นี่คือตัวอย่างทั้งหมด:

#include <array>
#include <iostream>

#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>

int main() {
    using boost::algorithm::join;
    using boost::adaptors::transformed;
    auto tostr = static_cast<std::string(*)(int)>(std::to_string);

    auto array = {1,2,3,4};
    std::cout << join(array | transformed(tostr), ",") << std::endl;

    return 0;
}

เครดิตให้กับกองกำลัง

คุณสามารถจัดการค่าประเภทใดก็ได้ดังนี้:

template<class Container>
std::string join(Container const & container, std::string delimiter) {
  using boost::algorithm::join;
  using boost::adaptors::transformed;
  using value_type = typename Container::value_type;

  auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
  return join(container | transformed(tostr), delimiter);
};

11

นี่เป็นเพียงความพยายามในการไขปริศนาที่ได้รับจากคำพูดของ 1800 INFORMATIONเกี่ยวกับวิธีแก้ปัญหาที่สองของเขาโดยขาดความเป็นสามัญไม่ใช่ความพยายามที่จะตอบคำถาม:

template <class Str, class It>
Str join(It begin, const It end, const Str &sep)
{
  typedef typename Str::value_type     char_type;
  typedef typename Str::traits_type    traits_type;
  typedef typename Str::allocator_type allocator_type;
  typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
                                       ostringstream_type;
  ostringstream_type result;

  if(begin!=end)
    result << *begin++;
  while(begin!=end) {
    result << sep;
    result << *begin++;
  }
  return result.str();
}

ทำงานบนเครื่องของฉัน (TM)


Visual Studio 2013 ได้รับความสับสนอย่างมากจาก typedefs ไม่ใช่ว่าคุณจะรู้เรื่องนั้นในปี 2552
Grault

@ เจส: ตอนนี้ฉันจ้องมองสิ่งนี้เป็นเวลา 5 นาทีแล้ว แต่คิดไม่ออกว่า VS จะไปถึงจุดไหน คุณสามารถเจาะจงมากขึ้นได้หรือไม่?
sbi

ฉันขอโทษฉันคิดว่าฉันได้พยายามรวมวัตถุโดยไม่มี << โอเวอร์โหลดซึ่งตอนนี้ฉันรู้แล้วว่าไม่เหมาะสมสำหรับรหัสของคุณ ฉันไม่สามารถทำให้รหัสของคุณรวบรวมด้วยเวกเตอร์ของสตริงได้ โปรดทราบว่า VS 2013 Community มีทั้งแบบฟรีและฟีเจอร์ครบครันซึ่งแตกต่างจากเวอร์ชัน "Express"
Grault

@Jes: สิ่งนี้ควรใช้ได้กับทุกประเภทที่สามารถสตรีมได้ (เช่นมีoperator<<โอเวอร์โหลด) แน่นอนว่าประเภทที่ไม่มีoperator<<อาจทำให้ข้อความแสดงข้อผิดพลาดสับสนมาก
sbi

น่าเสียดายที่สิ่งนี้ไม่ได้รวบรวม: join(v.begin(), v.end(), ","). การหักอาร์กิวเมนต์แม่แบบจะไม่ให้ผลลัพธ์ที่ถูกต้องหากsepอาร์กิวเมนต์เป็นสตริงลิเทอรัล ความพยายามของฉันที่ทางออกสำหรับปัญหานี้ ยังให้โอเวอร์โหลดตามช่วงที่ทันสมัยมากขึ้น
zett42

7

เทมเพลต / ไอเดียมากมาย ของฉันไม่ได้เป็นแบบทั่วไปหรือมีประสิทธิภาพ แต่ฉันก็มีปัญหาเดียวกันและต้องการที่จะโยนสิ่งนี้เข้าไปในส่วนผสมที่สั้นและหวาน ชนะด้วยจำนวนบรรทัดที่สั้นที่สุด ... :)

std::stringstream joinedValues;
for (auto value: array)
{
    joinedValues << value << ",";
}
//Strip off the trailing comma
std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);

1
ขอบคุณสำหรับรหัสที่เรียบง่าย อาจต้องการเปลี่ยนเป็น "อัตโนมัติ &" เพื่อหลีกเลี่ยงการทำสำเนาเพิ่มเติมและรับประสิทธิภาพที่เพิ่มขึ้นอย่างง่ายดาย
มิลลี่สมิ ธ

แทนที่จะใช้substr(...)ให้ใช้pop_back()เพื่อลบอักขระสุดท้ายจะชัดเจนและสะอาดขึ้นมาก
ifyalciner

4

หากต้องการทำstd::cout << join(myVector, ",") << std::endl;คุณสามารถทำสิ่งต่อไปนี้

template <typename C, typename T> class MyJoiner
{
    C &c;
    T &s;
    MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
public:
    template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
    template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
};

template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
{
    auto i = mj.c.begin();
    if (i != mj.c.end())
    {
        o << *i++;
        while (i != mj.c.end())
        {
            o << mj.s << *i++;
        }
    }

    return o;
}

template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
{
    return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
}

หมายเหตุโซลูชันนี้จะเข้าร่วมโดยตรงกับสตรีมเอาต์พุตแทนที่จะสร้างบัฟเฟอร์สำรองและจะทำงานกับทุกประเภทที่มีตัวดำเนินการ << เข้ากับ ostream

นอกจากนี้ยังใช้งานได้ที่ไหน boost::algorithm::join()ล้มเหลวเมื่อคุณมีvector<char*>ไฟล์vector<string>.


4
string s;
for (auto i : v)
    s += (s.empty() ? "" : ",") + to_string(i);

8
ยินดีต้อนรับสู่ Stack Overflow! แม้ว่าโค้ดนี้จะแก้ปัญหาได้ แต่ควรเพิ่มรายละเอียดและอธิบายวิธีการทำงานสำหรับผู้ที่อาจไม่เข้าใจโค้ดนี้
paper1111

1
คำตอบยอดนิยมในปัจจุบันไม่ได้ซับซ้อนมากไปกว่านี้และนี่คือคำตอบที่ใช้งานได้น้อยที่สุด / สะอาดที่สุด ไม่มีประสิทธิภาพเท่ากับstd::stringstreamอาร์เรย์ขนาดใหญ่เนื่องจากstringstreamจะสามารถจัดสรรหน่วยความจำในแง่ดีทำให้ประสิทธิภาพ O (n.log (n)) แทน O (n²) สำหรับอาร์เรย์ขนาดnสำหรับคำตอบนี้ นอกจากนี้ยังอาจจะไม่สร้างสตริงชั่วคราวstringstream to_string(i)
aberaud

2

ฉันชอบคำตอบของ 1800 อย่างไรก็ตามฉันจะย้ายการวนซ้ำครั้งแรกออกจากลูปเนื่องจากผลลัพธ์ของคำสั่ง if เปลี่ยนแปลงเพียงครั้งเดียวหลังจากการทำซ้ำครั้งแรก

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  A it = begin;
  if (it != end) 
  {
   result.append(*it);
   ++it;
  }

  for( ;
       it!=end;
       ++it)
  {
    result.append(t);
    result.append(*it);
  }
  return result;
}

แน่นอนว่าสิ่งนี้สามารถลดลงเป็นงบน้อยลงหากคุณต้องการ:

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  A it = begin;
  if (it != end) 
   result.append(*it++);

  for( ; it!=end; ++it)
   result.append(t).append(*it);
  return result;
}

คุณไม่ควรใช้ Post-Increment สำหรับประเภทตัววนซ้ำที่ไม่รู้จัก ซึ่งอาจมีราคาแพง (แน่นอนว่าเมื่อจัดการกับสตริงนั่นอาจไม่ได้สร้างความแตกต่างมากนัก แต่เมื่อคุณเรียนรู้นิสัยแล้ว ... )
sbi

การเพิ่มโพสต์นั้นใช้ได้ตราบเท่าที่คุณใช้ค่าชั่วคราวที่ส่งคืน เช่น "result.append (* it); ++ it;" มักจะมีราคาแพงพอ ๆ กับ "result.append (* it ++);" อันที่สองมีสำเนาของตัววนซ้ำอีกหนึ่งชุด
iain

อ๊ะฉันเพิ่งเห็นการเพิ่มขึ้นของโพสต์ใน for loop ข้อผิดพลาดในการคัดลอกและวาง ฉันได้แก้ไขโพสต์แล้ว
iain

1
@Ian: ตอนที่ฉันสอน C ++ ฉันตอกให้นักเรียนใช้++iยกเว้นที่ที่พวกเขาต้องการจริงๆi++เพราะนั่นเป็นวิธีเดียวที่พวกเขาจะไม่ลืมสิ่งนี้เมื่อมันสร้างความแตกต่าง (เหมือนกันกับฉัน BTW) พวกเขาเคยเรียนรู้ Java มาก่อนซึ่ง C-isms ทุกชนิดเป็นสมัยนิยมและใช้เวลาสองสามเดือน (1 การบรรยาย + การทำงานในห้องปฏิบัติการต่อสัปดาห์) แต่ในที่สุด พวกเขาเรียนรู้นิสัยที่จะใช้การเพิ่มขึ้นล่วงหน้า
sbi

1
@sbi: เห็นด้วยที่ฉันมักจะผิดนัดล่วงหน้าเช่นกันการโพสต์หลอกลวงมาจากการคัดลอกคนอื่นเพื่อวนซ้ำและเปลี่ยนมัน ในการตอบกลับครั้งแรกของฉันฉันคิดว่าคุณกังวลเกี่ยวกับ "result.append (* it ++)" ไม่ใช่สำหรับลูป ฉันรู้สึกอายเล็กน้อยที่เห็นการเพิ่มขึ้นของโพสต์ในลูป บางคนดูเหมือนจะปฏิบัติตามคำแนะนำในการไม่ใช้การเพิ่มโพสต์มากเกินไปและไม่เคยใช้หรือเปลี่ยนแปลงแม้ว่าจะเหมาะสมก็ตาม อย่างไรก็ตามฉันรู้ว่าตอนนี้คุณไม่ได้อยู่ในหมวดหมู่นี้
iain

2

มีความพยายามบางอย่างที่น่าสนใจในการนำเสนอวิธีแก้ปัญหาที่สวยงาม ฉันมีความคิดที่จะใช้สตรีมแบบเทมเพลตเพื่อตอบปัญหาภาวะที่กลืนไม่เข้าคายไม่ออกดั้งเดิมของ OP ได้อย่างมีประสิทธิภาพ แม้ว่านี่จะเป็นโพสต์เก่า แต่ฉันหวังว่าผู้ใช้ในอนาคตที่สะดุดกับสิ่งนี้จะพบว่าโซลูชันของฉันเป็นประโยชน์

ประการแรกคำตอบบางคำ (รวมถึงคำตอบที่ยอมรับ) ไม่ส่งเสริมการใช้งานซ้ำ เนื่องจาก C ++ ไม่ได้ให้วิธีการที่ยอดเยี่ยมในการรวมสตริงในไลบรารีมาตรฐาน (ที่ฉันเคยเห็น) สิ่งสำคัญคือต้องสร้างสิ่งที่ยืดหยุ่นและใช้ซ้ำได้ นี่คือภาพของฉัน:

// Replace with your namespace //
namespace my {
    // Templated join which can be used on any combination of streams, iterators and base types //
    template <typename TStream, typename TIter, typename TSeperator>
    TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) {
        // A flag which, when true, has next iteration prepend our seperator to the stream //
        bool sep = false;                       
        // Begin iterating through our list //
        for (TIter i = begin; i != end; ++i) {
            // If we need to prepend a seperator, do it //
            if (sep) stream << seperator;
            // Stream the next value held by our iterator //
            stream << *i;
            // Flag that next loops needs a seperator //
            sep = true;
        }
        // As a convenience, we return a reference to the passed stream //
        return stream;
    }
}

ในการใช้สิ่งนี้คุณสามารถทำสิ่งต่อไปนี้:

// Load some data //
std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);
params.push_back(4);

// Store and print our results to standard out //
std::stringstream param_stream;
std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl;

// A quick and dirty way to print directly to standard out //
my::join(std::cout, params.begin(), params.end(), ",") << std::endl;

โปรดสังเกตว่าการใช้สตรีมทำให้โซลูชันนี้มีความยืดหยุ่นอย่างไม่น่าเชื่อได้อย่างไรเนื่องจากเราสามารถจัดเก็บผลลัพธ์ของเราไว้ในสตรีมสตริงเพื่อเรียกคืนในภายหลังหรือเราสามารถเขียนลงใน standard out ไฟล์หรือแม้แต่การเชื่อมต่อเครือข่ายที่ใช้เป็นสตรีมโดยตรง ประเภทที่พิมพ์จะต้องทำซ้ำได้และเข้ากันได้กับสตรีมต้นทาง STL มีสตรีมที่หลากหลายซึ่งเข้ากันได้กับหลากหลายประเภท คุณจึงสามารถไปเมืองนี้ได้จริงๆ จากด้านบนของหัวของฉันเวกเตอร์ของคุณอาจเป็น int, float, double, string, int ที่ไม่ได้ลงนาม, SomeObject * และอื่น ๆ


1

ฉันได้สร้างไฟล์ส่วนหัวของตัวช่วยเพื่อเพิ่มการสนับสนุนการเข้าร่วมเพิ่มเติม

เพียงเพิ่มโค้ดด้านล่างลงในไฟล์ส่วนหัวทั่วไปของคุณและรวมไว้เมื่อจำเป็น

ตัวอย่างการใช้งาน:

/* An example for a mapping function. */
ostream&
map_numbers(ostream& os, const void* payload, generic_primitive data)
{
    static string names[] = {"Zero", "One", "Two", "Three", "Four"};
    os << names[data.as_int];
    const string* post = reinterpret_cast<const string*>(payload);
    if (post) {
        os << " " << *post;
    }
    return os;
}

int main() {
    int arr[] = {0,1,2,3,4};
    vector<int> vec(arr, arr + 5);
    cout << vec << endl; /* Outputs: '0 1 2 3 4' */
    cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */
    cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */
    cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */
    cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */
    string post = "Mississippi";
    cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */
    return 0;
}

รหัสเบื้องหลัง:

#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
using namespace std;

#define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; }
#define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T;

typedef void* ptr;

/** A union that could contain a primitive or void*,
 *    used for generic function pointers.
 * TODO: add more primitive types as needed.
 */
struct generic_primitive {
    GENERIC_PRIMITIVE_CLASS_BUILDER(int);
    GENERIC_PRIMITIVE_CLASS_BUILDER(ptr);
    union {
        GENERIC_PRIMITIVE_TYPE_BUILDER(int);
        GENERIC_PRIMITIVE_TYPE_BUILDER(ptr);
    };
};

typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive);
template<typename T>
class Join {
public:
    Join(const T& begin, const T& end,
            const string& separator = " ",
            mapping_funct_t mapping = 0,
            const void* payload = 0):
            m_begin(begin),
            m_end(end),
            m_separator(separator),
            m_mapping(mapping),
            m_payload(payload) {}

    ostream&
    apply(ostream& os) const
    {
        T begin = m_begin;
        T end = m_end;
        if (begin != end)
            if (m_mapping) {
                m_mapping(os, m_payload, *begin++);
            } else {
                os << *begin++;
            }
        while (begin != end) {
            os << m_separator;
            if (m_mapping) {
                m_mapping(os, m_payload, *begin++);
            } else {
                os << *begin++;
            }
        }
        return os;
    }
private:
    const T& m_begin;
    const T& m_end;
    const string m_separator;
    const mapping_funct_t m_mapping;
    const void* m_payload;
};

template <typename T>
Join<T>
join(const T& begin, const T& end,
     const string& separator = " ",
     ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0,
     const void* payload = 0)
{
    return Join<T>(begin, end, separator, mapping, payload);
}

template<typename T>
ostream&
operator<<(ostream& os, const vector<T>& vec) {
    return join(vec.begin(), vec.end()).apply(os);
}

template<typename T>
ostream&
operator<<(ostream& os, const list<T>& lst) {
    return join(lst.begin(), lst.end()).apply(os);
}

template<typename T>
ostream&
operator<<(ostream& os, const set<T>& s) {
    return join(s.begin(), s.end()).apply(os);
}

template<typename T>
ostream&
operator<<(ostream& os, const Join<T>& vec) {
    return vec.apply(os);
}

1

นี่คือโซลูชัน C ++ 11 ทั่วไปที่จะช่วยให้คุณทำได้

int main() {
    vector<int> v {1,2,3};
    cout << join(v, ", ") << endl;
    string s = join(v, '+').str();
}

รหัสคือ:

template<typename Iterable, typename Sep>
class Joiner {
    const Iterable& i_;
    const Sep& s_;
public:
    Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {}
    std::string str() const {std::stringstream ss; ss << *this; return ss.str();}
    template<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j);
};

template<typename I, typename S>
std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j) {
    auto elem = j.i_.begin();
    if (elem != j.i_.end()) {
        os << *elem;
        ++elem;
        while (elem != j.i_.end()) {
            os << j.s_ << *elem;
            ++elem;
        }
    }
    return os;
}

template<typename I, typename S>
inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}

1

ต่อไปนี้เป็นวิธีที่ง่ายและใช้ได้จริงในการแปลงองค์ประกอบจากvectora เป็น a string:

std::string join(const std::vector<int>& numbers, const std::string& delimiter = ",") {
    std::ostringstream result;
    for (const auto number : numbers) {
        if (result.tellp() > 0) { // not first round
            result << delimiter;
        }
        result << number;
    }
    return result.str();
}

คุณจำเป็นต้องสำหรับ#include <sstream>ostringstream


1

ขยายความพยายาม @sbi ในโซลูชันทั่วไปที่ไม่ จำกัดstd::vector<int>หรือชนิดสตริงส่งคืนเฉพาะ โค้ดที่แสดงด้านล่างสามารถใช้ได้ดังนี้:

std::vector<int> vec{ 1, 2, 3 };

// Call modern range-based overload.
auto str     = join( vec,  "," );
auto wideStr = join( vec, L"," );

// Call old-school iterator-based overload.
auto str     = join( vec.begin(), vec.end(),  "," );
auto wideStr = join( vec.begin(), vec.end(), L"," );

ในโค้ดดั้งเดิมการหักอาร์กิวเมนต์แม่แบบจะใช้ไม่ได้ในการสร้างประเภทสตริงที่ส่งคืนที่ถูกต้องหากตัวคั่นเป็นสตริงลิเทอรัล (ดังตัวอย่างด้านบน) ในกรณีนี้ typedefs เช่นStr::value_typeในส่วนของฟังก์ชันไม่ถูกต้อง รหัสจะถือว่าStrเป็นประเภทที่ชอบเสมอstd::basic_stringดังนั้นจึงเห็นได้ชัดว่าล้มเหลวสำหรับตัวอักษรสตริง

ในการแก้ไขปัญหานี้รหัสต่อไปนี้จะพยายามอนุมานเฉพาะประเภทอักขระจากอาร์กิวเมนต์ตัวคั่นและใช้สิ่งนั้นเพื่อสร้างประเภทสตริงส่งคืนเริ่มต้น สิ่งนี้ทำได้โดยใช้boost::range_valueซึ่งแยกประเภทองค์ประกอบจากประเภทช่วงที่กำหนด

#include <string>
#include <sstream>
#include <boost/range.hpp>

template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt >
Str join( InputIt first, const InputIt last, const Sep& sep )
{
    using char_type          = typename Str::value_type;
    using traits_type        = typename Str::traits_type;
    using allocator_type     = typename Str::allocator_type;
    using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >;

    ostringstream_type result;

    if( first != last )
    {
        result << *first++;
    }
    while( first != last ) 
    {
        result << sep << *first++;
    }
    return result.str();
}

ตอนนี้เราสามารถจัดเตรียมโอเวอร์โหลดตามช่วงได้อย่างง่ายดายซึ่งเพียงแค่ส่งต่อไปยังโอเวอร์โหลดตามตัววนซ้ำ:

template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange>
Str join( const InputRange &input, const Sep &sep )
{
    // Include the standard begin() and end() in the overload set for ADL. This makes the 
    // function work for standard types (including arrays), aswell as any custom types 
    // that have begin() and end() member functions or overloads of the standalone functions.
    using std::begin; using std::end;

    // Call iterator-based overload.
    return join( begin(input), end(input), sep );
}

Live Demo ที่ Coliru


0

อย่างที่ @capone ทำ

std::string join(const std::vector<std::string> &str_list , 
                 const std::string &delim=" ")
{
    if(str_list.size() == 0) return "" ;
    return std::accumulate( str_list.cbegin() + 1, 
                            str_list.cend(), 
                            str_list.at(0) , 
                            [&delim](const std::string &a , const std::string &b)
                            { 
                                return a + delim + b ;
                            }  ) ; 
}

template <typename ST , typename TT>
std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec)
{
    vector<TT> rst ;
    std::transform(ori_vec.cbegin() ,
                  ori_vec.cend() , back_inserter(rst) , 
                  [&op](const ST& val){ return op(val)  ;} ) ;
    return rst ;
}

จากนั้นเราสามารถเรียกสิ่งต่อไปนี้:

int main(int argc , char *argv[])
{
    vector<int> int_vec = {1,2,3,4} ;
    vector<string> str_vec = map<int,string>(to_string, int_vec) ;
    cout << join(str_vec) << endl ;
    return 0 ;
}

เช่นเดียวกับหลาม:

>>> " ".join( map(str, [1,2,3,4]) )

0

ฉันใช้อะไรแบบนี้

namespace std
{

// for strings join
string to_string( string value )
{
    return value;
}

} // namespace std

namespace // anonymous
{

template< typename T >
std::string join( const std::vector<T>& values, char delimiter )
{
    std::string result;
    for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx )
    {
        if( idx != 0 )
            result += delimiter;
        result += std::to_string( values[idx] );
    }
    return result;
}

} // namespace anonymous

0

ฉันเริ่มต้นด้วยคำตอบของ @ sbi แต่เวลาส่วนใหญ่ลงเอยด้วยการส่งสตริงผลลัพธ์ไปยังสตรีมดังนั้นจึงสร้างโซลูชันด้านล่างที่สามารถส่งไปยังสตรีมได้โดยไม่ต้องเสียค่าใช้จ่ายในการสร้างสตริงเต็มในหน่วยความจำ

ใช้ดังนี้:

#include "string_join.h"
#include <iostream>
#include <vector>

int main()
{
  std::vector<int> v = { 1, 2, 3, 4 };
  // String version
  std::string str = join(v, std::string(", "));
  std::cout << str << std::endl;
  // Directly piped to stream version
  std::cout << join(v, std::string(", ")) << std::endl;
}

โดย string_join.h คือ:

#pragma once

#include <iterator>
#include <sstream>

template<typename Str, typename It>
class joined_strings
{
  private:
    const It begin, end;
    Str sep;

  public:
    typedef typename Str::value_type char_type;
    typedef typename Str::traits_type traits_type;
    typedef typename Str::allocator_type allocator_type;

  private:
    typedef std::basic_ostringstream<char_type, traits_type, allocator_type>
      ostringstream_type;

  public:
    joined_strings(It begin, const It end, const Str &sep)
      : begin(begin), end(end), sep(sep)
    {
    }

    operator Str() const
    {
      ostringstream_type result;
      result << *this;
      return result.str();
    }

    template<typename ostream_type>
    friend ostream_type& operator<<(
      ostream_type &ostr, const joined_strings<Str, It> &joined)
    {
      It it = joined.begin;
      if(it!=joined.end)
        ostr << *it;
      for(++it; it!=joined.end; ++it)
        ostr << joined.sep << *it;
      return ostr;
    }
};

template<typename Str, typename It>
inline joined_strings<Str, It> join(It begin, const It end, const Str &sep)
{
  return joined_strings<Str, It>(begin, end, sep);
}

template<typename Str, typename Container>
inline joined_strings<Str, typename Container::const_iterator> join(
  Container container, const Str &sep)
{
  return join(container.cbegin(), container.cend(), sep);
}

0

ฉันได้เขียนโค้ดต่อไปนี้ มันขึ้นอยู่กับ C # string.join ทำงานร่วมกับ std :: string และ std :: wstring และเวกเตอร์หลายประเภท (ตัวอย่างในความคิดเห็น)

เรียกแบบนี้ว่า

 std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};

 std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');

รหัส:

// Generic Join template (mimics string.Join() from C#)
// Written by RandomGuy (stackoverflow) 09-01-2017
// Based on Brian R. Bondy anwser here:
// http://stackoverflow.com/questions/1430757/c-vector-to-string
// Works with char, wchar_t, std::string and std::wstring delimiters
// Also works with a different types of vectors like ints, floats, longs
template<typename T, typename D>
auto Join(const std::vector<T> &vToMerge, const D &delimiter)
{
    // We use std::conditional to get the correct type for the stringstream (char or wchar_t)
    // stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t>
    using strType =
        std::conditional<
        std::is_same<D, std::string>::value,
        char,
            std::conditional<
            std::is_same<D, char>::value,
            char,
            wchar_t
            >::type
        >::type;

    std::basic_stringstream<strType> ss;

    for (size_t i = 0; i < vToMerge.size(); ++i)
    {
        if (i != 0)
            ss << delimiter;
        ss << vToMerge[i];
    }
    return ss.str();
}

0

นี่คือวิธีง่ายๆในการแปลงเวกเตอร์ของจำนวนเต็มเป็นสตริง

#include <bits/stdc++.h>
using namespace std;
int main()
{
    vector<int> A = {1, 2, 3, 4};
    string s = "";
    for (int i = 0; i < A.size(); i++)
    {
        s = s + to_string(A[i]) + ",";
    }
    s = s.substr(0, s.length() - 1); //Remove last character
    cout << s;
}

0

เข้าร่วมโดยใช้ฟังก์ชันเทมเพลต

ฉันใช้ a template functionเพื่อเข้าร่วมvectorรายการและลบifคำสั่งที่ไม่จำเป็นออกโดยทำซ้ำเฉพาะรายการแรกที่จะสุดท้ายในรายการvectorจากนั้นจึงเข้าร่วมรายการสุดท้ายหลังการforวนซ้ำ นอกจากนี้ยังขัดขวางความจำเป็นในการใช้รหัสพิเศษเพื่อลบตัวคั่นพิเศษที่ส่วนท้ายของสตริงที่เข้าร่วม ดังนั้นจึงไม่มีifข้อความใดที่ชะลอการทำซ้ำและไม่มีตัวคั่นที่ไม่จำเป็นที่ต้องจัดระเบียบ

นี้ก่อให้เกิดการเรียกใช้ฟังก์ชันสง่างามในการเข้าร่วมvectorของstring, integerหรือdoubleอื่น ๆ

ฉันเขียนสองเวอร์ชัน: หนึ่งส่งคืนสตริง; อีกอันเขียนไปยังสตรีมโดยตรง

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

// Return a string of joined vector items.
template<typename T>
string join(const vector<T>& v, const string& sep)
{
    ostringstream oss;
    const auto LAST = v.end() - 1;
    // Iterate through the first to penultimate items appending the separator.
    for (typename vector<T>::const_iterator p = v.begin(); p != LAST; ++p)
    {
        oss << *p << sep;
    }
    // Join the last item without a separator.
    oss << *LAST;
    return oss.str();
}

// Write joined vector items directly to a stream.
template<typename T>
void join(const vector<T>& v, const string& sep, ostream& os)
{
    const auto LAST = v.end() - 1;
    // Iterate through the first to penultimate items appending the separator.
    for (typename vector<T>::const_iterator p = v.begin(); p != LAST; ++p)
    {
        os << *p << sep;
    }
    // Join the last item without a separator.
    os << *LAST;
}

int main()
{
    vector<string> strings
    {
        "Joined",
        "from",
        "beginning",
        "to",
        "end"
    };
    vector<int> integers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    vector<double> doubles{ 1.2, 3.4, 5.6, 7.8, 9.0 };

    cout << join(strings, "... ") << endl << endl;
    cout << join(integers, ", ") << endl << endl;
    cout << join(doubles, "; ") << endl << endl;

    join(strings, "... ", cout);
    cout << endl << endl;
    join(integers, ",  ", cout);
    cout << endl << endl;
    join(doubles, ";  ", cout);
    cout << endl << endl;

    return 0;
}

เอาต์พุต

Joined... from... beginning... to... end

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

1.2; 3.4; 5.6; 7.8; 9

Joined... from... beginning... to... end

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

1.2; 3.4; 5.6; 7.8; 9
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.