vector<int>
정수 (예 : {1,2,3,4}) 가있는 컨테이너가 있고 다음 형식의 문자열로 변환하고 싶습니다.
"1,2,3,4"
C ++에서 가장 깔끔한 방법은 무엇입니까? 파이썬에서 이것은 내가하는 방법입니다.
>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
답변:
확실히 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
대신 사용할 수도 있습니다 .
std::string s = ss.str()
. 당신이 원하는 경우 const char*
, 사용 s.c_str()
. (구문 적으로는 정확 하지만 전체 표현이 끝날 때 더 이상 존재하지 않을 임시를 가리키는 ss.str().c_str()
a const char*
를 제공합니다 . 아파요.)
#include <sstream>
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
}
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);
});
','
이어야합니다","
string
클래스에는 +
문자도 허용 할 수있는 연산자에 대한 오버로드 가 있습니다. 그래서 ','
괜찮습니다.
또 다른 대안은 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(","));
이상적으로는 문자 유형을 추론하고 문자열 스트림을 사용할 수있는 지점까지 템플릿을 작성하고 싶지만 아직 알아낼 수 없었습니다.
join
함수는 벡터에도 사용할 수 있습니까? 예를 들어주세요. 저는 C ++를 처음 사용합니다.
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);
};
이것은 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)에서 작동합니다.
operator<<
오버로드 됨)에서 작동합니다. 물론없는 유형 operator<<
은 매우 혼란스러운 오류 메시지를 유발할 수 있습니다.
join(v.begin(), v.end(), ",")
. sep
인수가 문자열 리터럴 인 경우 템플릿 인수 추론이 올바른 결과를 생성하지 않습니다 . 이 문제에 대한 해결책에 대한 나의 시도 . 또한보다 현대적인 범위 기반 과부하를 제공합니다.
많은 템플릿 / 아이디어. 내 것이 일반적이거나 효율적이지는 않지만, 같은 문제가 있었고 이것을 짧고 달콤한 것으로 믹스에 넣고 싶었습니다. 최단 라인 수에서 승리합니다 ... :)
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);
substr(...)
사용 pop_back()
하여 마지막 문자를 제거하면 훨씬 더 명확하고 깨끗해집니다.
원하는 경우 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>
.
string s;
for (auto i : v)
s += (s.empty() ? "" : ",") + to_string(i);
std::stringstream
있기 때문에 대형 배열 만큼 효율적 stringstream
이지 않으며, n
이 답변에 대한 크기 배열에 대해 O (n²) 대신 O (n.log (n)) 성능으로 이어집니다 . 또한에 stringstream
대한 임시 문자열을 빌드하지 않을 수 있습니다 to_string(i)
.
나는 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;
}
++i
정말로 필요한 곳을 제외하고 사용하도록 두 드렸습니다. i++
그것이 차이를 만들었을 때 잊지 않을 유일한 방법 이었기 때문입니다. (BTW, 저도 마찬가지였습니다.) 그들은 모든 종류의 C-ism이 유행하고 몇 달이 걸리는 Java를 배웠지 만 (주 1 회 강의 + 랩 작업) 결국 대부분의 그들은 사전 증가를 사용하는 습관을 배웠습니다.
문제에 대한 우아한 해결책을 제공하려는 몇 가지 흥미로운 시도가 있습니다. 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 * 등이 될 수 있습니다.
확장 조인 지원을 추가하기 위해 도우미 헤더 파일을 만들었습니다.
일반 헤더 파일에 아래 코드를 추가하고 필요할 때 포함하기 만하면됩니다.
사용 예 :
/* 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);
}
다음은 할 수있는 일반적인 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);}
다음은 간단한와의 요소를 변환하는 실용적인 방법입니다 vector
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
.
제한되지 않는 일반 솔루션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 );
}
@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]) )
나는 이와 같은 것을 사용한다
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
나는 @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);
}
다음 코드를 작성했습니다. 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();
}
를 사용 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