我们知道计算机存储英文字母,标点,数字用的是ascall码,128种用一个字节表示绰绰有余。而汉字远远不止128种,因此汉字需要两个字节表示。
1.gbk编码中汉字占两个字节。
2.utf-8中,一个汉字占三个字节。
> UTF规定:如果一个符号只占一个字节,那么这个8位字节的第一位就为0。如果为两个字节,那么规定第一个字节的前两位都为1,然后第一个字节的第三位为0,第二个字节的前两位为10,然后如果是三个字节的话,那么第一个字节的前三位为111,第四位为0,剩余的两个字节的前两位都为10。按照这样的算法去思考一个中文字符的UTF-8是怎么表示的:一个中文字符需要两个字节来表示,两个字节一共是16位,那么UTF-8下,两个字节是不够的,因为两个字节下,第一个字节已经占据了三位:110,然后剩余的一个字节占据了两位:10,现在就只剩下8位,与Unicode下的两个字节,16位去表示任意一个字符是相悖的,也就是Unicode下的16位减去UTF-8下的8位=8位,刚好差了一个字节的空间,所以就使用三个字节去表示非ANSI字符:三个字节下,一共是24位,第一个字节头四位是:1110,后两个字节的前两位都是:10,那么24位-8位=16位,刚好两个字节去表示Unicode下的任意一个非ANSI字符。
这里resize和reserve都是在string对象后开指定大小的可用空间,而不是把它的容量设置为指定大小。
resize改变了size大小,而reserve不改变.
resize空间小于size大小时会删除元素,reserve空间小于size时不会改变容量也不删除元素。
迭代器实现
#include <cctype>
class Solution {
public:
int StrToInt(string str) {
int ans = 0;
int i=0;
for (string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit) {
if (isdigit(*rit)) {
ans=ans+((*rit)-48)*pow(10,i++);
continue;
}
if (*rit == '+')
break;
else if (*rit == '-')
ans *= -1;
else
return 0;
}
return ans;
}
};
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
示例 1:
输入:num1 = “11”, num2 = “123”
输出:“134”
class Solution {
public:
string addStrings(string num1, string num2) {
int end1=num1.size()-1,end2=num2.size()-1;
int carry=0;
string ans;
//提前扩一波容
ans.reserve(num1.size()>num2.size()?num1.size()+1:num2.size()+1);
while(end1>=0||end2>=0)
{
int val1=end1>=0?num1[end1]-'0':0;
int val2=end2>=0?num2[end2]-'0':0;
int ret=val1+val2+carry;
carry=ret/10;
ans+=ret%10+'0';
--end1;
--end2;
}
if(carry)
ans+='1';
reverse(ans.begin(),ans.end());
return ans;
}
};
这里我们用数学中用竖式计算加法的方法,用carry表示进位,从后往前一步一步加到ans中,同时注意细节处理,最后反转一下ans得到答案。
这里我们用空格替换“/”,如果用多个字符替换呢?那么上述方法就显得很低效,因为不仅开空间还要往后挪数据。解决方法:以空间换时间,重新定义一个空字符,遍历两遍老字符。第一遍开空间,第二遍往空字符加数据。如下图
我们可以看到string的swap只需要改变指针指向就可以了,而std库里的需要创建一个临时对象还要拷贝赋值完成交换。
将string转化为c语言能够识别的const char*,如下打开文件并读取
#include<stdio.h>
#include<string>
using namespace std;
int main()
{
string s1("hello,world");
string s2(s1);
printf("%x\n",s1.c_str());
printf("%x\n",s2.c_str());
s1[2]='m';
printf("%x\n",s1.c_str());
printf("%x\n",s2.c_str());
s2[2]='m';
printf("%x\n",s1.c_str());
printf("%x\n",s2.c_str());
return 0;
}
下面代码在g++下运行结果
我们发现前面不修改s1和s2时两者地址是一样的,而修改s1中的字符时,操作系统此时才会给s1开辟一块独立的新空间把数据拷贝过去并修改。这就是所谓的写时拷贝技术。
每当我们为string分配内存时,我们总是要多分配一个空间用来存放这个引用计数的值,只要发生拷贝构造可是赋值时,这个内存的值就会加一。而在内容修改时,string类为查看这个引用计数是否为0,如果不为零,表示有人在共享这块内存,那么自己需要先做一份拷贝,然后把引用计数减去一,再把数据拷贝过来。
class string {
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str+_size;
}
string(const char* str = "") :
_size(strlen(str))
{
_capacity = _size;
_str = new char[_capacity + 1+1];
strcpy(_str, str);
}
/*string(const string& s)
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}*/
//现代写法
void swap(string& tmp)
{
::swap(_str, tmp._str);
::swap(_size, tmp._size);
::swap(_capacity, tmp._capacity);
}
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(tmp);
_str[_capacity + 1]++;
}
~string()
{
delete[]_str;
_str = nullptr;
_size = _capacity = 0;
}
//实现赋值操作
string& operator=(const string& s)
{
if (this != &s)
{
/*delete[] _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);*/
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
const char* c_str()const
{
return _str;
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
char& operator[](size_t pos)
{
return _str[pos];
}
const char& operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 0 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
/*size_t end = _size;
while (end >= pos)
{
_str[end + 1] = _str[end];
--end;
}*/
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
return*this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
size_t end = _size + len;
while (end >= pos + len)
{
_str[end] = _str[end + len];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
}
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
}
}
size_t find(char ch,size_t pos = 0)const
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (ch == _str[i])
{
return i;
}
}
return npos;
}
size_t find(const char* sub, size_t pos = 0)const
{
assert(sub);
assert(pos < _size);
const char* ptr = strstr(_str + pos, sub);
if (ptr == NULL)
{
return npos;
}
else
{
return ptr - _str;
}
}
string substr(size_t pos, size_t len = npos)const
{
assert(pos < _size);
size_t reallen = len;
if (len == npos || pos + len > _size)
{
reallen = _size - pos;
}
string sub;
for (size_t i = 0; i < reallen; i++)
{
sub += _str[pos + i];
}
return sub;
}
private:
char* _str;
size_t _size;
size_t _capacity;
public:
static size_t npos;
};
size_t string::npos = -1;
istream& operator>>(istream& in, string& s)
{
char ch;
ch = in.get();
const size_t N = 32;
char buff[N];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i=N-1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
buff[i] = '\0';
s += buff;
}