[英]Convert a vector<int> to a string
我有一個包含整數的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'
絕對不如 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::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);
});
另一種選擇是使用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(","));
理想情況下,我想模板化到可以推斷字符類型的程度,並使用字符串流,但我還想不通。
使用 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);
};
這只是試圖解決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)。
很多模板/想法。 我的不是通用的或高效的,但我只是遇到了同樣的問題,我想把它作為簡短而甜蜜的東西加入混合中。 它以最短的行數獲勝...... :)
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);
string s;
for (auto i : v)
s += (s.empty() ? "" : ",") + to_string(i);
如果你想做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<< 的類型一起使用。
當您使用vector<char*>
而不是vector<string>
時,這也適用於boost::algorithm::join()
失敗的情況。
我喜歡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;
}
有一些有趣的嘗試為這個問題提供了一個優雅的解決方案。 我有一個想法,使用模板化流來有效地回答 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;
請注意流的使用如何使這個解決方案變得非常靈活,因為我們可以將結果存儲在字符串流中以便以后回收,或者我們可以直接寫入標准輸出、文件,甚至寫入作為流實現的網絡連接。 被打印的類型必須是可迭代的並且與源流兼容。 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
中的元素轉換為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();
}
您需要為ostringstream
#include <sstream>
。
在不限於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();
}
這是將整數向量轉換為字符串的簡單方法。
#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;
}
我使用template
function
來連接vector
項,並通過僅迭代vector
的第一個到倒數第二個項,然后在for
循環之后加入最后一個項來刪除不必要的if
語句。 這也不需要額外的代碼來刪除連接字符串末尾的額外分隔符。 因此,沒有減慢迭代的if
語句,也沒有需要整理的多余分隔符。
這會產生一個優雅的函數調用來連接string
、 integer
或double
等的vector
。
我寫了兩個版本:一個返回一個字符串; 另一個直接寫入流。
#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
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v{{1,2,3,4}};
std::string str;
// ----->
if (! v.empty())
{
str = std::to_string(*v.begin());
for (auto it = std::next(v.begin()); it != v.end(); ++it)
str.append("," + std::to_string(*it));
}
// <-----
std::cout << str << "\n";
}
為什么這里的答案如此復雜可笑
string vec2str( vector<int> v){
string s="";
for (auto e: v){
s+=to_string(e);
s+=',';
}
s.pop_back();
return s;
}
使用數字庫中的std::accumulate ( #include <numeric>
) 解決此問題的另一種方法:
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto comma_fold = [](std::string a, int b) { return std::move(a) + ',' + std::to_string(b); };
std::string s = std::accumulate(std::next(v.begin()), v.end(),
std::to_string(v[0]), // start with first element
comma_fold);
std::cout << s << std::endl; // 1,2,3,4,5,6,7,8,9,10
簡單的解決方案/黑客......不優雅但它有效
const auto vecToString = [](std::vector<int> input_vector)
{
std::string holder = "";
for (auto s : input_vector){
holder += std::to_string(s);
if(input_vector.back() != s){
holder += ", ";
}
}
return holder;
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.