StringUtil
位置
- src/StringUtil.cpp
- src/StringUtil.hh
功能
- 这是一个工具类,这个类提供了std::string对象的三个方法,其中trim方法和split方法,是std::string类他自身本来所没有提供的。 还有一个是将格式化字符串和具体的可变参数转为实际的字符串
class StringUtil {
public:
static std::string vform(const char* format, va_list args);
static std::string trim(const std::string& s);
static unsigned int split(std::vector<std::string>& v,
const std::string& s, char delimiter,
unsigned int maxSegments = INT_MAX);
template<typename T> static unsigned int split(T& output,
const std::string& s, char delimiter,
unsigned int maxSegments = INT_MAX) ;
};
实现
- 首先看一下StringUtil::vform方法的实现
// 将格式化字符串format和可变参数args转换为具体的字符串类型
std::string StringUtil::vform(const char* format, va_list args) {
size_t size = 1024;
char* buffer = new char[size];
while (1) {
va_list args_copy;
va_copy(args_copy, args);
int n = vsnprintf(buffer, size, format, args_copy);
va_end(args_copy);
if ((n > -1) && (static_cast<size_t>(n) < size)) {
std::string s(buffer);
delete [] buffer;
return s;
}
// 可能是空间小了,那么分配多一点空间再次尝试
size = (n > -1) ? n + 1 : size * 2;
delete [] buffer;
buffer = new char[size];
}
}
vsprintf_s函数需要缓冲区来接收格式化后的结果,因为不知道具体有多少参数,导致最后格式化后的字符串长度是不同的,这里的解决方法是进行尝试,当失败的时候,就选择更大的缓冲区来进行接收
- StringUtil.trim实现
std::string StringUtil::trim(const std::string& s) {
static const char* whiteSpace = " \t\r\n";
// test for null string
if(s.empty())
return s;
// 找左边第一个不在whiteSpaces列表中的字符
std::string::size_type b = s.find_first_not_of(whiteSpace);
if(b == std::string::npos) // No non-spaces
return "";
// 然后从最后向前查找,第一个不在whiteSpaces中的字符
std::string::size_type e = s.find_last_not_of(whiteSpace);
// 返回剩下的字符
return std::string(s, b, e - b + 1);
}
可以看出这个方法可以取出首尾两端的空白字符(包括回车、制表符,换行符)
- StringUtil.split实现
unsigned int StringUtil::split(std::vector<std::string>& v,
const std::string& s,
char delimiter,
unsigned int maxSegments) {
v.clear();
std::back_insert_iterator<std::vector<std::string> > it(v); //创建后插迭代器适配器利用下面建好的模板方法
return split(it, s, delimiter, maxSegments); //调用下面的方法模板
}
template<typename T>
static unsigned int split(T& output,
const std::string& s,
char delimiter,
unsigned int maxSegments = INT_MAX) {
std::string::size_type left = 0; //找到第一个有效的字符
unsigned int i;
for(i = 1; i < maxSegments; i++) {
std::string::size_type right = s.find(delimiter, left); //当前 delimeter所有的位置
if (right == std::string::npos) {
break;
}
*output++ = s.substr(left, right - left);
left = right + 1;
}
*output++ = s.substr(left);
return i;
}
主要实现思路,是记录两个位置,一个位置是第一个有效的字符,,程序中是left变量,另一个位置是当前 delimeter所有的位置,程序中是right变量,然后将[left, right)之间的字符拷贝到output中,然后移动 left, right到下一个位置(符合上述规则的位置),要小心最后一次找不到delimeter的情况。
上面的实现思路其实解决不了连续出现两个都是delimeter字符的情况。比如"1,2,3“这种情况,最后的结果 中必然存在一个空字符,还解决不了当最后一个字符是delimeter的情况,因为这个方法,最后没有进行检测,那么 假如输入是"1,2,3,4,",那么最后也必然会有一个空字符