不要使用memset初始化string(一定别这么干)
最后更新于:2022-04-01 06:41:51
##实战c++中的string系列--不要使用memset初始化string(一定别这么干)
参考链接:
[http://www.cppblog.com/qinqing1984/archive/2009/08/07/92479.html](http://www.cppblog.com/qinqing1984/archive/2009/08/07/92479.html)
百度百科第一次这么给力:
void *memset(void *s, int ch, size_t n);
函数解释:将s中前n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。
memset() 函数常用于内存空间初始化:
~~~
char str[100];
memset(str,0,100);
~~~
用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘
~~~
memset(a, '\0', sizeof(a));
~~~
memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度:
~~~
char a[100], b[50];
memcpy(b, a, sizeof(b)); //注意如用sizeof(a),会造成b的内存地址溢出。
~~~
strcpy就只能拷贝字符串了,它遇到’\0’就结束拷贝:
~~~
char a[100], b[50];
strcpy(a,b);
~~~
如用strcpy(b,a),要注意a中的字符串长度(第一个‘\0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。
下面开始:
~~~
class Material
{
public:
Material(){ setDefaults();}
void setDefaults(){ memset(this,0,sizeof(*this));}
int mark;
char materialName[256]; // material name
Vector3 ambient; // ambient
Vector3 diffuse; // diffuse
Vector3 specular; // specular
int shininess; //
float alpha; //
bool isSpecular;
char textureName[256]; // texture name
char textureTransName[256]; // transparent texture name
};
~~~
这段代码完美无瑕。再看看下面的:
~~~
class Material
{
public:
Material(){ setDefaults();}
void setDefaults(){ memset(this,0,sizeof(*this));}
int mark;
std::string materialName; // material name
Vector3 ambient; // ambient
Vector3 diffuse; // diffuse
Vector3 specular; // specular
int shininess; //
float alpha; //
bool isSpecular;
std::string textureName; // texture name
std::string textureTransName; // transparent texture name
};
~~~
上面的代码会造成内存泄露:
所以对于C++的std::string来说,要使用C++风格的初始化。
在网上看到这样一条评论,觉得有道理:
**任何类都不能用memset, 一旦暴力,就等于你强奸了她的内部数据,她已经崩溃了**
将string用于switch语句(c++做C#的事儿, switch中break还是return厉害)
最后更新于:2022-04-01 06:41:49
##实战c++中的string系列--将string用于switch语句(c++做C#的事儿, switch中break还是return厉害)
作为一个C++程序员,或是出于习惯,或是出于无奈,你多少次这么写:
~~~
if (!strcmp(pszValue, "Value X"))
DoThis();
else if (!strcmp(pszValue, "Value Y"))
DoThat();
else if (!strcmp(pszValue, "Value Z"))
DoSomethingElse();
else
DontKnowWhatToDo();
~~~
你千百次的问,如果这个时候可以使用switch多好呢?
~~~
switch(strValue)
{
case "Value X":
DoThis();
break;
case "Value Y":
DoThat();
break;
case "Value Z";
DoSomethingElse();
break;
default:
DontKnowWhatToDo();
break;
~~~
上面这段代码在C Sharp中是合法的,但是作为一个C++程序员,你只能无奈和无奈。
下面就是用enum和std::map完成这个愿望!
~~~
#include <map>
#include <string>
#include <iostream.h>
// Value-Defintions of the different String values
static enum StringValue { evNotDefined,
evStringValue1,
evStringValue2,
evStringValue3,
evEnd };
// Map to associate the strings with the enum values
static std::map<std::string, StringValue> s_mapStringValues;
// User input
static char szInput[_MAX_PATH];
// Intialization
static void Initialize();
int main(int argc, char* argv[])
{
// Init the string map
Initialize();
// Loop until the user stops the program
while(1)
{
// Get the user's input
cout << "Please enter a string (end to terminate): ";
cout.flush();
cin.getline(szInput, _MAX_PATH);
// Switch on the value
switch(s_mapStringValues[szInput])
{
case evStringValue1:
cout << "Detected the first valid string." << endl;
break;
case evStringValue2:
cout << "Detected the second valid string." << endl;
break;
case evStringValue3:
cout << "Detected the third valid string." << endl;
break;
case evEnd:
cout << "Detected program end command. "
<< "Programm will be stopped." << endl;
return(0);
default:
cout << "'" << szInput
<< "' is an invalid string. s_mapStringValues now contains "
<< s_mapStringValues.size()
<< " entries." << endl;
break;
}
}
return 0;
}
void Initialize()
{
s_mapStringValues["First Value"] = evStringValue1;
s_mapStringValues["Second Value"] = evStringValue2;
s_mapStringValues["Third Value"] = evStringValue3;
s_mapStringValues["end"] = evEnd;
cout << "s_mapStringValues contains "
<< s_mapStringValues.size()
<< " entries." << endl;
}
~~~
这里有个特别重要的技巧,那就是为什么把enumeration的第一个设为evNotDefined ?
首先我们要明确std::map::operator[] 的作用:
1 设置一个key的value
2 取值。这个时候需要注意,若不存在,才会被插入。
即程序中对于s_mapStringValues,如果szInput 是新的,将会被插入。
并且 the value默认为0 。
如果enumeration第一项为evStringValue1,任何一个未知的string value 都会导致一个有效的switch case。所以我们才这么干。
==============================================================
这里还有个小问题 讨论一下,就是switch语句中return厉害还是break厉害:
代码:
~~~
switch(s_mapStringValues[szInput])
{
case evStringValue1:
cout << "Detected the first valid string." << endl;
return 0;//还会执行break吗?
break;
case evStringValue2:
cout << "Detected the second valid string." << endl;
break;
case evStringValue3:
cout << "Detected the third valid string." << endl;
break;
case evEnd:
cout << "Detected program end command. "
<< "Programm will be stopped." << endl;
return(0);
default:
cout << "'" << szInput
<< "' is an invalid string. s_mapStringValues now contains "
<< s_mapStringValues.size()
<< " entries." << endl;
break;
}
~~~
测试,表面,return了 就不会break了。
函数返回局部变量string(引用局部string,局部string的.c_str()函数)
最后更新于:2022-04-01 06:41:47
##实战c++中的string系列--函数返回局部变量string(引用局部string,局部string的.c_str()函数)
当函数返回字符串的时候,我们可以定义返回string和string&。
1写一个返回string引用的函数
~~~
std::string & TestStringReference()
{
std::string loal_str = "holy shit";
return loal_str;
}
~~~
这个函数当然是错误的,编译器会提示我们:
返回局部变量或临时变量的地址: loal_str
即不能返回局部变量的引用。
2写一个返回string的函数(函数返回局部变量string的时候能不能被引用?)
~~~
std::string TestStringReference()
{
std::string strTest = "This is a test.";
return strTest;
}
~~~
那么对于上述函数的返回值可以被引用吗?
代码说话:
~~~
#include<iostream>
#include<string>
std::string TestStringReference()
{
std::string strTest = "This is a test.";
return strTest;
}
int main()
{
std::string& strRefer = TestStringReference();
std::cout << "strRefer:" << strRefer << std::endl;
return 0;
}
~~~
代码 完美运行。
实际上返回的不是局部变量,而是编译器新构造的临时对象。
3返回string的函数直接调用.c_str()
上面说了,返回的“局部”string可以被引用的,那么返回的“局部”string直接调用.c_str()会有什么效果恩?
~~~
#include<iostream>
#include<string>
std::string TestStringC_STR()
{
std::string strTest = "This is a test.";
return strTest;
}
int main()
{
const char *pc = TestStringC_STR().c_str();
std::cout << pc << std::endl;
return 0;
}
~~~
上面代码编译器不会报错!
但是等等,别高兴太早,看看输出结果,为空,不是我们期望的。
关键是,我们没有将TestStringC_STR()的结果赋给一个string对象就直接获取其指针了,这时,系统并不会为string调用拷贝构造函数或是赋值函数,返回的string仍然只是一个临时对象的状态,它会在完成对pc的赋值后被销毁,这时其内部的数据也不会存在了。
解决方法:先用一个string接收函数的返回值,然后再调用c_str()方法:
~~~
#include<iostream>
#include<string>
std::string TestStringC_STR()
{
std::string strTest = "This is a test.";
return strTest;
}
int main()
{
std::string str1 = TestStringC_STR();
const char *pc = str1.c_str();
std::cout << pc << std::endl;
return 0;
}
~~~
string的连接(+= or append or push_back)
最后更新于:2022-04-01 06:41:44
##实战c++中的string系列--string的连接(+= or append or push_back)
string的连接也是经常用到的,string重载了一些运算符:
首先看一看重载+运算符,用于串联两个字符串对象:
源码:
~~~
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator> operator+(
const basic_string<CharType, Traits, Allocator>& _Left,
const basic_string<CharType, Traits, Allocator>& _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator> operator+(
const basic_string<CharType, Traits, Allocator>& _Left,
const CharType* _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator> operator+(
const basic_string<CharType, Traits, Allocator>& _Left,
const CharType _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator> operator+(
const CharType* _Left,
const basic_string<CharType, Traits, Allocator>& _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator> operator+(
const CharType _Left,
const basic_string<CharType, Traits, Allocator>& _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator>&& operator+(
const basic_string<CharType, Traits, Allocator>& _Left,
const basic_string<CharType, Traits, Allocator>&& _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator>&& operator+(
const basic_string<CharType, Traits, Allocator>&& _Left,
const basic_string<CharType, Traits, Allocator>& _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator>&& operator+(
const basic_string<CharType, Traits, Allocator>&& _Left,
const basic_string<CharType, Traits, Allocator>&& _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator>&& operator+(
const basic_string<CharType, Traits, Allocator>&& _Left,
const CharType *_Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator>&& operator+(
const basic_string<CharType, Traits, Allocator>&& _Left,
CharType _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator>&& operator+(
const CharType *_Left,
const basic_string<CharType, Traits, Allocator>&& _Right
);
template<class CharType, class Traits, class Allocator>
basic_string<CharType, Traits, Allocator>&& operator+(
CharType _Left,
const basic_string<CharType, Traits, Allocator>&& _Rig
~~~
所以使用时,注意事项:
~~~
#include<iostream>
#include<string>
int main()
{
std::string my_str = "holiday";
std::string my_str_add = "error" + "error";//错误
std::string my_str_add2 = my_str + "right";
std::string my_str_add3 = my_str + "right" + "right";
std::string my_str_add4 = "right" + my_str;
std::string my_str_add5 = "error" + "error" + my_str;//错误
return 0;
}
~~~
下面开始正题!
**+=**
****将字符追加到字符串****
~~~
basic_string<CharType, Traits, Allocator>& operator+=(
value_type _Ch
);
basic_string<CharType, Traits, Allocator>& operator+=(
const value_type* _Ptr
);
basic_string<CharType, Traits, Allocator>& operator+=(
const basic_string<CharType, Traits, Allocator>& _Right
);
~~~
**append**
****添加字符为字符串的末尾****
~~~
basic_string<CharType, Traits, Allocator>& append(
const value_type* _Ptr
);
basic_string<CharType, Traits, Allocator>& append(
const value_type* _Ptr,
size_type _Count
);
basic_string<CharType, Traits, Allocator>& append(
const basic_string<CharType, Traits, Allocator>& _Str,
size_type _Off,
size_type _Count
);
basic_string<CharType, Traits, Allocator>& append(
const basic_string<CharType, Traits, Allocator>& _Str
);
basic_string<CharType, Traits, Allocator>& append(
size_type _Count,
value_type _Ch
);
template<class InputIterator>
basic_string<CharType, Traits, Allocator>& append(
InputIterator _First,
InputIterator _Last
);
basic_string<CharType, Traits, Allocator>& append(
const_pointer _First,
const_pointer _Last
);
basic_string<CharType, Traits, Allocator>& append(
const_iterator _First,
const_iterator _Last
);
~~~
有多个重载函数,因此多种使用方法:
~~~
string str1a ( "Hello " );
const char *cstr1a = "Out There ";
str1a.append ( cstr1a );
string str1b ( "Hello " );
const char *cstr1b = "Out There ";
str1b.append ( cstr1b , 3 );
string str1c ( "Hello " ), str2c ( "Wide World " );
str1c.append ( str2c , 5 , 5 );
string str1d ( "Hello " ), str2d ( "Wide " ), str3d ( "World " );
str1d.append ( str2d );
str1d += str3d;
string str1e ( "Hello " );
str1e.append ( 4 , '!' );
string str1f ( "Hello " ), str2f ( "Wide World " );
str1f.append ( str2f.begin ( ) + 5 , str2f.end ( ) - 1 );
~~~
**push_back**
****将元素添加到该字符串的末尾****
~~~
void push_back(
value_type _Ch
);
~~~
这里需要注意的是,以下代码是错误的:
~~~
my_str.push_back("123");//错误
my_str.push_back('1');//ok
~~~
CDuiString和string的转换(duilib中的cduistring)
最后更新于:2022-04-01 06:41:42
##实战c++中的string系列--CDuiString和string的转换(duilib中的cduistring)
使用所duilib的人定会知道cduistring类型,先看看这个类是怎么定义的:
~~~
class UILIB_API CDuiString
{
public:
enum { MAX_LOCAL_STRING_LEN = 127/*63*/ };
CDuiString();
CDuiString(const TCHAR ch);
CDuiString(const CDuiString& src);
CDuiString(LPCTSTR lpsz, int nLen = -1);
~CDuiString();
void Empty();
int GetLength() const;
bool IsEmpty() const;
TCHAR GetAt(int nIndex) const;
void Append(LPCTSTR pstr);
void Assign(LPCTSTR pstr, int nLength = -1);
LPCTSTR GetData() const;
void SetAt(int nIndex, TCHAR ch);
operator LPCTSTR() const;
TCHAR operator[] (int nIndex) const;
const CDuiString& operator=(const CDuiString& src);
const CDuiString& operator=(const TCHAR ch);
const CDuiString& operator=(LPCTSTR pstr);
#ifdef _UNICODE
const CDuiString& CDuiString::operator=(LPCSTR lpStr);
const CDuiString& CDuiString::operator+=(LPCSTR lpStr);
#else
const CDuiString& CDuiString::operator=(LPCWSTR lpwStr);
const CDuiString& CDuiString::operator+=(LPCWSTR lpwStr);
#endif
CDuiString operator+(const CDuiString& src) const;
CDuiString operator+(LPCTSTR pstr) const;
const CDuiString& operator+=(const CDuiString& src);
const CDuiString& operator+=(LPCTSTR pstr);
const CDuiString& operator+=(const TCHAR ch);
bool operator == (LPCTSTR str) const;
bool operator != (LPCTSTR str) const;
bool operator <= (LPCTSTR str) const;
bool operator < (LPCTSTR str) const;
bool operator >= (LPCTSTR str) const;
bool operator > (LPCTSTR str) const;
int Compare(LPCTSTR pstr) const;
int CompareNoCase(LPCTSTR pstr) const;
void MakeUpper();
void MakeLower();
CDuiString Left(int nLength) const;
CDuiString Mid(int iPos, int nLength = -1) const;
CDuiString Right(int nLength) const;
int Find(TCHAR ch, int iPos = 0) const;
int Find(LPCTSTR pstr, int iPos = 0) const;
int ReverseFind(TCHAR ch) const;
int Replace(LPCTSTR pstrFrom, LPCTSTR pstrTo);
int __cdecl Format(LPCTSTR pstrFormat, ...);
int __cdecl SmallFormat(LPCTSTR pstrFormat, ...);
protected:
LPTSTR m_pstr;
TCHAR m_szBuffer[MAX_LOCAL_STRING_LEN + 1];
};
~~~
下面使用方法:
~~~
1 Append(LPCTSTR str) 在原字符串基础上追加一个字符串;
CDuiString dui_str;
dui_str.Append(_T("我是中国人!"));
dui_str.Append(_T("我爱中国!"));
2 Assign(LPCSTR pstr ,int nLength ) 在pstr字符串的nLength位置后的东西全部剪切掉不要; config.Assign(config, config.GetLength()-1);
3 Format(LPCSTR pstrformat, ...); 按照pstrformat字符串的格式,导入其他类型变量
CDuiString folde_path; folde_path.Format(_T("%ls"), std_string);
4 GetLength()采集一个变量的宽度(大小);
config.GetLength();
~~~
很多时候 难免用到CDuiString和string的转换。
我们应该注意到,CDuiString类有个方法:
~~~
LPCTSTR GetData() const;
~~~
可以通过这个方法,把CDuiString变为LPCTSTR ;
所以下一步只是如何把LPCTSTR 转为string了。
首先写一个StringFromLPCTSTR函数,完成转换:
~~~
std::string StringFromLPCTSTR(LPCTSTR str) {
#ifdef _UNICODE
int size_str = WideCharToMultiByte(CP_UTF8, 0, str, -1, 0, 0, NULL, NULL);
char* point_new_array = new char[size_str];
WideCharToMultiByte(CP_UTF8, 0, str, -1, point_new_array, size_str, NULL, NULL);
std::string return_string(point_new_array);
delete[] point_new_array;
point_new_array = NULL;
return return_string;
#else
return std::string(str);
#endif
}
~~~
下面就可以完成duicstring到string的转换了:
~~~
CDuiString download_link = msg.pSender->GetUserData();
std::string download_link_str = StringFromLPCTSTR(download_link.GetData());
~~~
std:vector<char> 和std:string相互转换(vector to stringstream)
最后更新于:2022-04-01 06:41:40
##实战c++中的string系列--std:vector<char> 和std:string相互转换(vector to stringstream)
有时候也会遇到std:vector与转std:string 相互转换的情况。
首先看一下`vector<char>`如何转string:
~~~
std::vector<char> *data = response->getResponseData();
std::string res;
//方法一
for (int i = 0;i<data->size();++i) {
res+=(*data)[i];
}
res+='\0';
std:cout << res;
//方法二
std::vector<char> *data = response->getResponseData();
std::string res;
res.insert(res.begin(), data->begin(), data->end());
std::cout << res;
//方法三
std::vector<char> *data = response->getResponseData();
std::string res;
const char* s = &(*data->begin());
res = std::string(s, data->size());
std::cout << res;
//方法四
string ch = "what a fucking day!";
vector <char> ta;
ta.resize(ch.size());
ta.assign(ch.begin(),ch.end());
~~~
string 转vector就会更容易:
~~~
vector <char> ta = {‘a’, 'b', 'c'};
ch.clear();
ch.assign(ta.begin(),ta.end());
~~~
================================================================
**vector to stringstream**
~~~
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <iterator>
// Dummy std::vector of strings
std::vector<std::string> sentence;
sentence.push_back("aa");
sentence.push_back("ab");
// Required std::stringstream object
std::stringstream ss;
// Populate
std::copy(sentence.begin(), sentence.end(),std::ostream_iterator<std::string>(ss,"\n"));
// Display
std::cout<<ss.str()<<std::endl;
~~~
string到LPCWSTR的转换
最后更新于:2022-04-01 06:41:38
##实战c++中的string系列--string到LPCWSTR的转换
今天再来介绍一下如何从string到LPCWSTR的转换。
LPCWSTR是什么类型呢?
看看如何定义的:
~~~
typedef const wchar_t* LPCWSTR;
~~~
顾名思义就是:
LPCWSTR是一个指向unicode编码字符串的32位指针,所指向字符串是wchar型,而不是char型。
比如说MessageBoxW的第二、第三个参数就是LPCWSTR类型。
~~~
`MessageBoxW(__in_opt HWND hWnd, __in_opt LPCWSTR lpText,
__in_opt LPCWSTR lpCaption, __in UINT uType)`
~~~
所以问题来了,有一个string类型的字符串,如何通过MessageBoxW进行显示呢?这就需要string到LPCWSTR类型的转换了!!
~~~
string image_path = "c:\\avi.png";
size_t size = image_path.length();
wchar_t *buffer = new wchar_t[size + 1];
MultiByteToWideChar(CP_ACP, 0, response->image_path.c_str(), size, buffer, size * sizeof(wchar_t));
buffer[size] = 0; //确保以 '\0' 结尾
::MessageBox(NULL, buffer, NULL, NULL);
delete buffer;
buffer = null;
~~~
看到了吧 又一次用了MultiByteToWideChar函数。所以牢记这个函数的用法。
[http://blog.csdn.net/wangshubo1989/article/details/49210385](http://blog.csdn.net/wangshubo1989/article/details/49210385)
std::string与MFC中CString的转换
最后更新于:2022-04-01 06:41:35
##实战c++中的string系列--std::string与MFC中CString的转换
搞过MFC的人都知道cstring,给我们提供了很多便利的方法。
CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作的时候方便了很多。不管怎样,使用CString有很多特殊的技巧,特别是对于纯C背景下走出来的程序员来说有点难以学习。
但是很多情况下,我们还是需要cstring和string的转换。
分两步:
1把cstring转为char数组
2根据char数组,构造自己的string(记得释放内存)
~~~
std::string CStringToSTDStr(const CString& theCStr)
{
const int theCStrLen = theCStr.GetLength();
char *buffer = (char*)malloc(sizeof(char)*(theCStrLen+1));
memset((void*)buffer, 0, sizeof(buffer));
WideCharToMultiByte(CP_UTF8, 0, static_cast<cstring>(theCStr).GetBuffer(), theCStrLen, buffer, sizeof(char)*(theCStrLen+1), NULL, NULL);
std::string STDStr(buffer);
free((void*)buffer);
return STDStr;
}
~~~
而string转cstring那就很轻松了:
~~~
string str="abcde";
CString cstr(str.c_str());
~~~
[](http://blog.csdn.net/wangshubo1989/article/details/50274079#)[](http://blog.csdn.net/wangshubo1989/article/details/50274079# "分享到QQ空间")[](http://blog.csdn.net/wangshubo1989/article/details/50274079# "分享到新浪微博")[](http://blog.csdn.net/wangshubo1989/article/details/50274079# "分享到腾讯微博")[](http://blog.csdn.net/wangshubo1989/article/details/50274079# "分享到人人网")[](http://blog.csdn.net/wangshubo1989/article/details/50274079# "分享到微信")
string的遍历(使用下标还是iterator)
最后更新于:2022-04-01 06:41:33
##实战c++中的string系列--string的遍历(使用下标还是iterator)
迭代器提供了访问容器中对象的方法。例如,可以使用一对迭代器指定list或vector中的一定范围的对象。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器也可以是那些定义了operator*()以及其他类似于指针的操作符地方法的类对象.
我们都知道可以用下标运算来访问string对象和vector对象。而另外还有一种更通用的方法也可以实现这样的方法。名曰:迭代器(iterator)。
类似于指针,迭代器也提供了对对象的间接访问。就迭代器而言,其对象是容器中的元素或者string中的字符。使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素。迭代器有有效和无效之分,有效的迭代器指向某个元素或者容器中尾元素的下一个位置。其他情况均为无效。
和指针不一样的是,迭代器不是使用取址符,而是使用begin和end成员。
结合解引用和成员访问操作
解引用迭代器可以获得迭代器所指的对象,如果该对象的类型恰好是类,那么就可以访问这个类的成员。例如:
(*it).empty();
这里要注意*it一定要加圆括号,否则会出错。如果不加,那么这句话的意思就变成了访问it的empty成员,但是it是个迭代器,没有empty成员。C++11中提供了箭头运算符->,箭头运算符把解引用和成员访问两个操作结合在了一起。故iter->mem 等价于(*iter).mem。
强烈注意:一旦使用了迭代器的循环体,那就不要向迭代器所属的容器添加元素。
迭代器的算术运算:
iter + n 迭代器加上一个整数后仍是一个迭代器,在这里迭代器和指针很像,可以理解成地址上的加减。
iter1 - iter2 两个迭代器相减的结果是它们之间的距离。即所指向位置的距离。
,>= 迭代器关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前,则说前者小于后者。
==============================================================
为何string vector可以使用下标访问,还设计了迭代器模式呢?
个人觉得:
**1、STL设置的初衷是,算法 容器分离**
**2、迭代器更通用些,有的容器对象不支持下标**
**3、一些方法,比如erase只能传递iterator**
就vector来说差别不大,迭代器其实就是原生指针,但是就C++整体容器来说还是迭代器好,像list这些容器等等,迭代器给你提供一个中间层,抽象掉各容器间的差异,让你能以相同的方式访问容器
string的初始化、删除、转大小写(construct erase upper-lower)
最后更新于:2022-04-01 06:41:31
##实战c++中的string系列--string的初始化、删除、转大小写(construct erase upper-lower)
string是有迭代器设计模式的,我还没有体会到迭代器带给我的好处,很多时候使用类似数组索引的方法就可以完成任务。
**场景1:删除string所有的大写字母**
这里用到erase方法:
~~~
#include<iostream>
#include<cctype>
#include<string>
using namespace std;
int main()
{
string str = "This IS A trick";
for(string::iterator iter = str.begin(); iter != str.end();++iter){
if(isupper(*iter)){
str.erase(iter);
--iter;
}
}
for(string::iterator iter = str.begin();
iter != str.end();++iter)
cout<<*iter<<" ";
return 0;
}
~~~
上面的代码会错误,原因之前博客《[没有躲过的坑–vector使用erase后迭代器变成野指针](http://blog.csdn.net/wangshubo1989/article/details/50121059 "erase陷阱")》说过,即erase后,迭代器指向的位置。
**场景2:初始化string**
之前博客讨论过string的初始化问题,朝花夕拾:
~~~
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz" + 'A';
int t = 'A';
string s1 = "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz" + t;
cout << t << endl;
cout << s << endl;
cout << s1 << endl;
return 0;
}
~~~
输出结果出乎意料:
65
tuvwxyz
tuvwxyz
这里需要提醒的是弄清楚字符和字符串的区别,千万别把双引号写成了单引号。
原因:
char 类型转成整形
对const char *进行指针偏移后的指针传给string的构造函数
**场景3:string中的字母转大小写**
常规的for循环就不写了,直接写一个很少用到的:
~~~
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
string str = "heLLo"; //可以不包含<string>
transform(str.begin(), str.end(), str.begin(), toupper);
cout<<str<<endl;//错误,因为没包含<string>
cout<<str.c_str()<<endl;
transform(str.begin(), str.end(), str.begin(), tolower);
cout<<str.c_str()<<endl;
}
~~~
这里需要注意的是,cout不能直接输出string。
这就引出个重要的话题,不包含string头文件,string可以用;包含string头文件,string也可以用?
我猜想是标准库不断更新的原因,也为了兼容之前的老版本
string的分割、替换(类似string.split或是explode())
最后更新于:2022-04-01 06:41:29
##实战c++中的string系列--string的分割、替换(类似string.split或是explode())
对一个字符串根据某个字符进行分割也是在实战中经常遇到的问题,也是面试中经常会被人提及的。
如果你是个C Sharp程序员,你会知晓string.split函数,有下面这些重载:
1) public string[] Split(params char[] separator)
2) public string[] Split(char[] separator, int count)
3) public string[] Split(char[] separator, StringSplitOptions options)
4) public string[] Split(string[] separator, StringSplitOptions options)
5) public string[] Split(char[] separator, int count, StringSplitOptions options)
6) public string[] Split(string[] separator, int count, StringSplitOptions options)
如果你是个PHP程序员,你也会使用explode方法。
但是如果你是C++程序员,或是进行C++开发,那么这里的string就没有现成的分割方法。我们需要自行实现。
~~~
const vector<string> explode(const string& s, const char& c)
{
string buff{""};
vector<string> v;
for(auto n:s)
{
if(n != c) buff+=n;
elseif(n == c && buff != "") { v.push_back(buff); buff = ""; }
}
if(buff != "") v.push_back(buff);
return v;
}
//使用自定义的字符串分割函数
int main()
{
string str{"the quick brown fox jumps over the lazy dog"};
vector<string> v{explode(str, ' ')};
for(auto n:v) cout << n << endl;
return 0;
}
//输出如下:
the
quick
brown
fox
...
~~~
下面是另一种形式:
~~~
int split(const string& str, vector<string>& ret_, string sep = ",")
{
if (str.empty())
{
return 0;
}
string tmp;
string::size_type pos_begin = str.find_first_not_of(sep);
string::size_type comma_pos = 0;
while (pos_begin != string::npos)
{
comma_pos = str.find(sep, pos_begin);
if (comma_pos != string::npos)
{
tmp = str.substr(pos_begin, comma_pos - pos_begin);
pos_begin = comma_pos + sep.length();
}
else
{
tmp = str.substr(pos_begin);
pos_begin = comma_pos;
}
if (!tmp.empty())
{
ret_.push_back(tmp);
tmp.clear();
}
}
return 0;
}
~~~
=============================================================
其他语言的string也有replace的方法,那么再c++中我们也可以自己实现这个方法:
~~~
string replace(const string& str, const string& src, const string& dest)
{
string ret;
string::size_type pos_begin = 0;
string::size_type pos = str.find(src);
while (pos != string::npos)
{
cout <<"replacexxx:" << pos_begin <<" " << pos <<"\n";
ret.append(str.data() + pos_begin, pos - pos_begin);
ret += dest;
pos_begin = pos + 1;
pos = str.find(src, pos_begin);
}
if (pos_begin < str.length())
{
ret.append(str.begin() + pos_begin, str.end());
}
return ret;
}
~~~
================================================================
最后介绍一个C中的函数,用于截取字符串:
原型:extern char *strtok(char *s, char *delim);
~~~
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
~~~
输出:
Splitting string “- This, a sample string.” into tokens:
This
a
sample
string
string的替换、查找(一些与路径相关的操作)
最后更新于:2022-04-01 06:41:26
##实战c++中的string系列--string的替换、查找(一些与路径相关的操作)
今天继续写一些string操作。
string给我们提供了很多的方法,但是每在使用的时候,就要费些周折。
场景1:
得到一个std::string full_path = “D:\program files\csdn”,但是我想得到”D:\program files\vagaa”这个路径。
这就需要字符串的替换
~~~
std::string full_path = "D:\\program files\\csdn"
const size_t last_slash_idx1 = full_path .find_last_of("\\/");
if (std::string::npos != last_slash_idx1)
{
full_path.erase(last_slash_idx1, std::string::npos);
}
std::string my_path = full_path + "\\vagaa";
~~~
这里用到了string的** find_last_of**方法,并使用了**erase**方法进行删除。
场景2:
得到一个std::string full_image_path = “D:\program files\csdn\vagaa.png”,但是我想根据这个路径得到文件的扩展名。
我们可以使用find_last_of+erase方法进行,还可以这么做:
~~~
std::string full_image_path = "D:\\program files\\csdn\\vagaa.png"
std::string file_extension_name;
size_t i = full_image_path .rfind('.', file_path.length());
if (i != string::npos) {
file_extension_name = full_image_path .substr(i + 1, full_image_path .length() - i);
}
~~~
此时用到了string的**rfind**方法和**substr**方法
场景3
===============================================================
基本知识:
出自博客[http://www.cnblogs.com/xFreedom/archive/2011/05/16/2048037.html](http://www.cnblogs.com/xFreedom/archive/2011/05/16/2048037.html)
**string类的构造函数:**
string(const char *s); //用c字符串s初始化
string(int n,char c); //用n个字符c初始化
此外,string类还支持默认构造函数和复制构造函数,如string s1;string s2=”hello”;都是正确的写法。当构造的string太长而无法表达时会抛出length_error异常 ;
**string类的字符操作:**
const char &operator[](int n)const;
const char &at(int n)const;
char &operator[](int n);
char &at(int n);
operator[]和at()均返回当前字符串中第n个字符的位置,但at函数提供范围检查,当越界时会抛出out_of_range异常,下标运算符[]不提供检查访问。
const char *data()const;//返回一个非null终止的c字符数组
const char *c_str()const;//返回一个以null终止的c字符串
int copy(char *s, int n, int pos = 0) const;//把当前串中以pos开始的n个字符拷贝到以s为起始位置的字符数组中,返回实际拷贝的数目
**string的特性描述:**
int capacity()const; //返回当前容量(即string中不必增加内存即可存放的元素个数)
int max_size()const; //返回string对象中可存放的最大字符串的长度
int size()const; //返回当前字符串的大小
int length()const; //返回当前字符串的长度
bool empty()const; //当前字符串是否为空
void resize(int len,char c);//把字符串当前大小置为len,并用字符c填充不足的部分
**string类的输入输出操作:**
string类重载运算符operator>>用于输入,同样重载运算符operator<<用于输出操作。
函数getline(istream &in,string &s);用于从输入流in中读取字符串到s中,以换行符’\n’分开。
**string的赋值:**
string &operator=(const string &s);//把字符串s赋给当前字符串
string &assign(const char *s);//用c类型字符串s赋值
string &assign(const char *s,int n);//用c字符串s开始的n个字符赋值
string &assign(const string &s);//把字符串s赋给当前字符串
string &assign(int n,char c);//用n个字符c赋值给当前字符串
string &assign(const string &s,int start,int n);//把字符串s中从start开始的n个字符赋给当前字符串
string &assign(const_iterator first,const_itertor last);//把first和last迭代器之间的部分赋给字符串
**string的连接:**
string &operator+=(const string &s);//把字符串s连接到当前字符串的结尾
string &append(const char *s); //把c类型字符串s连接到当前字符串结尾
string &append(const char *s,int n);//把c类型字符串s的前n个字符连接到当前字符串结尾
string &append(const string &s); //同operator+=()
string &append(const string &s,int pos,int n);//字符串中从pos开始的n个字符连接到当前字符串的结尾
string &append(int n,char c); //在当前字符串结尾添加n个字符c
string &append(const_iterator first,const_iterator last);//把迭代器first和last之间的部分连接到当前字符串的结尾
**string的比较:**
bool operator==(const string &s1,const string &s2)const;//比较两个字符串是否相等
运算符”>”,”=”,”<=”,”!=”均被重载用于字符串的比较;
int compare(const string &s) const;//比较当前字符串和s的大小
int compare(int pos, int n,const string &s)const;//比较当前字符串从pos开始的n个字符组成的字符串与s的大小
int compare(int pos, int n,const string &s,int pos2,int n2)const;
int compare(const char *s) const;
int compare(int pos, int n,const char *s) const;
int compare(int pos, int n,const char *s, int pos2) const;
compare函数在>时返回1,<时返回-1,==时返回0
**string的子串:**
string substr(int pos = 0,int n = npos) const;//返回pos开始的n个字符组成的字符串
**string的交换:**
void swap(string &s2); //交换当前字符串与s2的值
**string类的查找函数:**
int find(char c, int pos = 0) const;//从pos开始查找字符c在当前字符串的位置
int find(const char *s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
int find(const char *s, int pos, int n) const;//从pos开始查找字符串s中前n个字符在当前串中的位置
int find(const string &s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
//查找成功时返回所在位置,失败返回string::npos的值
int rfind(char c, int pos = npos) const;//从pos开始从后向前查找字符c在当前串中的位置
int rfind(const char *s, int pos = npos) const;
int rfind(const char *s, int pos, int n = npos) const;
int rfind(const string &s,int pos = npos) const;
//从pos开始从后向前查找字符串s中前n个字符组成的字符串在当前串中的位置,成功返回所在位置,失败时返回string::npos的值
int find_first_of(char c, int pos = 0) const;//从pos开始查找字符c第一次出现的位置
int find_first_of(const char *s, int pos = 0) const;
int find_first_of(const char *s, int pos, int n) const;
int find_first_of(const string &s,int pos = 0) const;
//从pos开始查找当前串中第一个在s的前n个字符组成的数组里的字符的位置。查找失败返回string::npos
int find_first_not_of(char c, int pos = 0) const;
int find_first_not_of(const char *s, int pos = 0) const;
int find_first_not_of(const char *s, int pos,int n) const;
int find_first_not_of(const string &s,int pos = 0) const;
//从当前串中查找第一个不在串s中的字符出现的位置,失败返回string::npos
int find_last_of(char c, int pos = npos) const;
int find_last_of(const char *s, int pos = npos) const;
int find_last_of(const char *s, int pos, int n = npos) const;
int find_last_of(const string &s,int pos = npos) const;
int find_last_not_of(char c, int pos = npos) const;
int find_last_not_of(const char *s, int pos = npos) const;
int find_last_not_of(const char *s, int pos, int n) const;
int find_last_not_of(const string &s,int pos = npos) const;
//find_last_of和find_last_not_of与find_first_of和find_first_not_of相似,只不过是从后向前查找
**string类的替换函数:**
string &replace(int p0, int n0,const char *s);//删除从p0开始的n0个字符,然后在p0处插入串s
string &replace(int p0, int n0,const char *s, int n);//删除p0开始的n0个字符,然后在p0处插入字符串s的前n个字符
string &replace(int p0, int n0,const string &s);//删除从p0开始的n0个字符,然后在p0处插入串s
string &replace(int p0, int n0,const string &s, int pos, int n);//删除p0开始的n0个字符,然后在p0处插入串s中从pos开始的n个字符
string &replace(int p0, int n0,int n, char c);//删除p0开始的n0个字符,然后在p0处插入n个字符c
string &replace(iterator first0, iterator last0,const char *s);//把[first0,last0)之间部分替换为字符串s
string &replace(iterator first0, iterator last0,const char *s, int n);//把[first0,last0)之间的部分替换为s的前n个字符
string &replace(iterator first0, iterator last0,const string &s);//把[first0,last0)之间的部分替换为串s
string &replace(iterator first0, iterator last0,int n, char c);//把[first0,last0)之间部分替换为n个字符c
string &replace(iterator first0, iterator last0,const_iterator first, const_iterator last);//把[first0,last0)之间的部分替换成[first,last)之间的字符串
**string类的插入函数:**
string &insert(int p0, const char *s);
string &insert(int p0, const char *s, int n);
string &insert(int p0,const string &s);
string &insert(int p0,const string &s, int pos, int n);
//前4个函数在p0位置插入字符串s中pos开始的前n个字符
string &insert(int p0, int n, char c);//此函数在p0处插入n个字符c
iterator insert(iterator it, char c);//在it处插入字符c,返回插入后迭代器的位置
void insert(iterator it, const_iterator first, const_iterator last);//在it处插入[first,last)之间的字符
void insert(iterator it, int n, char c);//在it处插入n个字符c
**string类的删除函数**
iterator erase(iterator first, iterator last);//删除[first,last)之间的所有字符,返回删除后迭代器的位置
iterator erase(iterator it);//删除it指向的字符,返回删除后迭代器的位置
string &erase(int pos = 0, int n = npos);//删除pos开始的n个字符,返回修改后的字符串
**string类的迭代器处理:**
string类提供了向前和向后遍历的迭代器iterator,迭代器提供了访问各个字符的语法,类似于指针操作,迭代器不检查范围。
用string::iterator或string::const_iterator声明迭代器变量,const_iterator不允许改变迭代的内容。常用迭代器函数有:
const_iterator begin()const;
iterator begin(); //返回string的起始位置
const_iterator end()const;
iterator end(); //返回string的最后一个字符后面的位置
const_iterator rbegin()const;
iterator rbegin(); //返回string的最后一个字符的位置
const_iterator rend()const;
iterator rend(); //返回string第一个字符位置的前面
rbegin和rend用于从后向前的迭代访问,通过设置迭代器string::reverse_iterator,string::const_reverse_iterator实现
指定浮点数有效数字并转为string
最后更新于:2022-04-01 06:41:24
##实战c++中的string系列--指定浮点数有效数字并转为string
上一篇博客讲了好几种方法进行number到string的转换,这里再单独说一下float或是double到string的转换。
还是处于控件显示的原因,比如说要显示文件的大小,我们从服务器可以获得这个文件的总bytes。这样就需要我们根据实际情况是显示bytes、kb、mb等单位。
常用的做法就是把num_bytes/1024,这个时候往往会得到浮点型,浮点型转string也没问题,但是如果你需要保留这个浮点型的一位或是几位小数,怎么操作会方便快捷呢?
你进行了相关搜索,但是很多人给你的回答都是要么使用cout, 要么使用printf进行格式化输出。
**我们使用的是stringstream**
Stringstreams allow manipulators and locales to customize the result of these operations so you can easily change the format of the resulting string
~~~
#include <iomanip>
#include <locale>
#include <sstream>
#include <string> // this should be already included in <sstream>
// Defining own numeric facet:
class WithComma: public numpunct<char> // class for decimal numbers using comma instead of point
{
protected:
char do_decimal_point() const { return ','; } // change the decimal separator
};
// Conversion code:
double Number = 0.12; // Number to convert to string
ostringstream Convert;
locale MyLocale( locale(), new WithComma);// Crate customized locale
Convert.imbue(MyLocale); // Imbue the custom locale to the stringstream
Convert << fixed << setprecision(3) << Number; // Use some manipulators
string Result = Convert.str(); // Give the result to the string
// Result is now equal to "0,120"
~~~
**setprecision**
控制输出流显示浮点数的有效数字个数 ,如果和fixed合用的话,可以控制小数点右面的位数
但是这里需要注意的是头文件:
~~~
#include <iomanip>
~~~
string与整型或浮点型互转
最后更新于:2022-04-01 06:41:22
##实战c++中的string系列--string与整型或浮点型互转
教科书中很少会提到string与int或是float的相互转换,但是在实际工程中会经常遇到,尤其在做UI控件显示的时候。比如说你要在edit控件中显示一个数值,那你就需要把这个数值首先转为string,然后再将这个string付给edit控件。
网上你会找到很多的转换方法,个人觉得效率差不多的情况下,简洁最好。
这里主要用到的是**stringstreams**:
stringstream 是 C++ 提供的另一个字串型的串流(stream)物件,和之前学过的 iostream、fstream 有类似的操作方式。要使用 stringstream, 必須先加入這一行:
~~~
#include <sstream>
~~~
stringstream 主要是用在將一個字串分割,可以先用 clear( )以及 str( ) 將指定字串設定成一开始的內容,再用 >> 把个別的资料输出,例如:
~~~
string s;
stringstream ss;
int a, b, c;
getline(cin, s);
ss.clear();
ss.str(s);
ss >> a >> b >> c;
~~~
下面就言归正传。
**1、stringstreams中number to string**
主要是两步走:
把number输出到stream
从stream中得到string
~~~
int Number = 123;
string Result;
ostringstream convert;
convert << Number;
Result = convert.str();
~~~
可以将上述代码缩略成一句话:
~~~
int Number = 123;
string String = static_cast<ostringstream*>( &(ostringstream() << Number) )->str();
~~~
这里需要说明的是,number不限于int,float一样可以工作
**2、stringstreams中string to number**
同样需要两步走:
根据string构造一个stream
将value 读到变量中
~~~
string Text = "456";
int Result;
istringstream convert(Text);
if ( !(convert >> Result) )
{
Result = 0; //if that fails set 'Result' to 0
}
~~~
同样,也可以对上面的代码进行简化:
~~~
string Text = "456";
int Number;
if ( ! (istringstream(Text) >> Number) ) Number = 0;
~~~
**3、C++11中number string互转**
C++11为我们提供了更为便利的方法:
整型、浮点型转string
**std::to_string**
重载如下:
string to_string (int val);
string to_string (long val);
string to_string (long long val);
string to_string (unsigned val);
string to_string (unsigned long val);
string to_string (unsigned long long val);
string to_string (float val);
string to_string (double val);
string to_string (long double val);
**字符串转整型:**
**stoi, stol, stoll**
**字符串转浮点型:**
**stof, stod, stold**
~~~
int number = 123;
string text = to_string(number);
text = "456"
number = stoi(number);
~~~
**4、C - stdio中的string与number转换**
Number to String
~~~
int Number = 123;
char Result[16];
sprintf ( Result, "%d", Number );
~~~
String to Number
~~~
char Text[] = "456";
int Result;
sscanf ( Text, "%d", &Result );
~~~
**5、C - stdlib中的string与number转换**
itoa
atoi
atol
atof
strtol
strtoul
strtod
但需要注意的是,上面的几个函数并非标准,尽量少用。
前言
最后更新于:2022-04-01 06:41:19
> 原文出处:[实战c++中的string系列专栏文章](http://blog.csdn.net/column/details/wangshubostring.html)
> 作者:[王书博](http://blog.csdn.net/wangshubo1989)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# 实战c++中的string系列
> 本专栏主要记录和讲诉实际工作中,关于string的一些操作,一些tips和一些tricks.