vector <int>를 문자열로 변환


93

vector<int>정수 (예 : {1,2,3,4}) 가있는 컨테이너가 있고 다음 형식의 문자열로 변환하고 싶습니다.

"1,2,3,4"

C ++에서 가장 깔끔한 방법은 무엇입니까? 파이썬에서 이것은 내가하는 방법입니다.

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

답변:


95

확실히 Python만큼 우아하지는 않지만 C ++의 Python만큼 우아한 것은 없습니다.

당신은 사용할 수 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대신 사용할 수도 있습니다 .


v.size ()가 아니라 array.size ()를 의미한다고 생각합니다.
Mark Elliot

1
ya 어떤 벡터가 호출 되든.
Brian R. Bondy

2
이어야합니다 std::string s = ss.str(). 당신이 원하는 경우 const char*, 사용 s.c_str(). (구문 적으로는 정확 하지만 전체 표현이 끝날 때 더 이상 존재하지 않을 임시를 가리키는 ss.str().c_str()a 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
나는 이것이 파이썬의 조인과 완전히 같지 않다고 생각합니다. 끝에 추가 ","를 삽입합니다.
1800 정보

2
동등하지는 않지만 우아합니다 (사실 더 그렇게 생각하지만 그것은 단지 의견입니다).
Martin York

20
분명히 우아함은 주관적입니다. 따라서 당신과 다른 두 사람이 작동하지 않는 더 길고 더 반복적 인 코드를 선호한다면 더 우아합니다. ;-p
Steve Jessop

1
string :: substr 멤버 함수를 사용하여 마지막 쉼표를 무시할 수 있습니다. 결과 변수에 0부터 n-1까지의 하위 문자열을 할당합니다.
Dan

@SteveJessop : 더 좋아?
Martin York

24

std :: accumulate를 사용할 수 있습니다. 다음 예를 고려하십시오.

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::copyostream_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

대답에서 반복자를 사전 증가로 변경할 수 있습니까?
밀리 스미스

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

Praetorian에 대한 크레딧 .

다음과 같이 모든 값 유형을 처리 할 수 ​​있습니다.

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은 typedef에 의해 매우 혼란스러워집니다. 당신은 알고 있었다하지 않는 것이 그 2009 년
Grault

@Jes : 나는 지금 이것을 5 분 동안 쳐다보고 있었지만, VS가 무엇을 넘어갈 지 알 수 없었다. 더 자세하게 얘기해 주 시겠어요?
sbi

죄송합니다. << 오버로드없이 개체 조인을 시도한 것 같습니다. 이제 코드에 적합하지 않다는 것을 알고 있습니다. 코드가 문자열 벡터로 컴파일되지 않도록 할 수는 없습니다. 참고로 VS 2013 커뮤니티는 "Express"버전과 달리 무료이며 기능이 풍부합니다.
Grault 2015-04-11

@Jes : 스트리밍 할 수있는 모든 유형 (예 : operator<<오버로드 됨)에서 작동합니다. 물론없는 유형 operator<<은 매우 혼란스러운 오류 메시지를 유발할 수 있습니다.
sbi apr

불행히도 이것은 컴파일되지 않습니다 : 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
단순한 코드에 감사드립니다. 여분의 복사본을 피하고 쉽게 성능을 향상시키기 위해 "auto &"로 변경할 수 있습니다.
밀리 스미스

을 사용하는 대신 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에 operator <<가있는 모든 유형에서 작동합니다.

곳도 작동 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이지 않으며, n이 답변에 대한 크기 배열에 대해 O (n²) 대신 O (n.log (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;
}

알 수없는 반복기 유형에 대해 사후 증가를 사용해서는 안됩니다. 비용이 많이들 수 있습니다. (물론, 문자열을 다룰 때 그다지 큰 차이가 없을 수도 있습니다.하지만 일단 습관을 배우면 ...)
sbi

반환되는 임시 값을 사용하는 한 포스트 증가는 괜찮습니다. 예 : "result.append (* it); ++ it;" 거의 항상 "result.append (* it ++);"만큼 비쌉니다. 두 번째에는 반복자의 추가 사본이 하나 있습니다.
iain 09-09-17

죄송합니다. for 루프에서 포스트 증가를 발견했습니다. 복사 및 붙여 넣기 오류입니다. 게시물을 수정했습니다.
iain 09-09-17

1
@Ian : 제가 C ++를 가르쳤을 때, 저는 학생들이 ++i정말로 필요한 곳을 제외하고 사용하도록 두 드렸습니다. i++그것이 차이를 만들었을 때 잊지 않을 유일한 방법 이었기 때문입니다. (BTW, 저도 마찬가지였습니다.) 그들은 모든 종류의 C-ism이 유행하고 몇 달이 걸리는 Java를 배웠지 만 (주 1 회 강의 + 랩 작업) 결국 대부분의 그들은 사전 증가를 사용하는 습관을 배웠습니다.
sbi

1
@sbi : 항상 사전 증분을 기본으로한다는 데 동의했습니다. 불량 postincrement는 루프를 위해 다른 사람을 복사하여 변경 한 것입니다. 내 첫 번째 회신에서 나는 당신이 for 루프가 아니라 "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;

스트림을 사용하면 결과를 나중에 회수하기 위해 stringstream에 저장하거나 표준 출력, 파일 또는 스트림으로 구현 된 네트워크 연결에 직접 쓸 수 있으므로이 솔루션이 어떻게이 솔루션을 매우 유연하게 만드는지 주목하십시오. 인쇄되는 유형은 단순히 반복 가능하고 소스 스트림과 호환 가능해야합니다. STL은 다양한 유형과 호환되는 다양한 스트림을 제공합니다. 그래서 당신은 이것으로 정말로 마을에 갈 수 있습니다. 내 머리 꼭대기에서 벡터는 int, float, double, string, unsigned 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를 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

제한되지 않는 일반 솔루션std::vector<int> 또는 특정 반환 문자열 유형 에서 @sbi의 시도를 확장 합니다. 아래에 제시된 코드는 다음과 같이 사용할 수 있습니다.

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"," );

원래 코드에서 구분 기호가 문자열 리터럴 인 경우 (위의 샘플에서와 같이) 올바른 반환 문자열 유형을 생성하기 위해 템플릿 인수 추론이 작동하지 않습니다. 이 경우 Str::value_type함수 본문 과 같은 typedef 가 올바르지 않습니다. 코드 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 );
}

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

템플릿 기능을 사용하여 결합

를 사용 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.