输入重载>>让string直接输入,像内置类型一样;从标准输入流读取字符,遇到' '或'\0'就停止
1. istream& operator>>(istream& in, string& s) 2. { 3. s.clear(); 4. char ch; 5. ch = in.get(); 6. while (ch != ' ' && ch != '\0') 7. { 8. s += ch; 9. ch = in.get(); 10. } 11. return in; 12. }
1. #pragma once 2. #define _CRT_SECURE_NO_WARNINGS 3. #include<iostream> 4. #include<assert.h> 5. #include<stdlib.h> 6. 7. using namespace std; 8. //定义一个新的命名空间,和全局命名空间隔离开 9. namespace delia 10. { 11. class string 12. { 13. public: 14. 15. typedef char* iterator; 16. 17. //迭代器 18. iterator begin() 19. { 20. return _str; 21. } 22. 23. //迭代器 24. iterator end() 25. { 26. return _str + _size; 27. } 28. 29. //const迭代器 30. iterator begin() const 31. { 32. return _str; 33. } 34. 35. //const迭代器 36. iterator end() const 37. { 38. return _str + _size; 39. } 40. 41. //构造函数 42. string(const char* str = "") 43. { 44. _str = new char[strlen(str) + 1];//1是留给'/0'的,_size最大为申请字节数-1,_capacity也为申请字节数-1 45. strcpy(_str, str); 46. 47. _size = strlen(str); 48. _capacity = _size; 49. } 50. 51. //现代拷贝构造 52. string(const string& s) 53. :_str(nullptr) 54. , _size(0) 55. , _capacity(0) 56. { 57. string tmp(s._str); 58. swap(tmp); 59. } 60. 61. //赋值运算符重载 62. string& operator=(string s) 63. { 64. swap(s); 65. return *this; 66. } 67. 68. //析构函数 69. ~string() 70. { 71. delete[] _str; 72. _str = nullptr; 73. _size = 0; 74. _capacity = 0; 75. } 76. 77. //实现交换函数 78. void swap(string& s) 79. { 80. ::swap(_str, s._str);//用::指定调用全局的swap函数即库里的swap函数 81. ::swap(_size, s._size); 82. ::swap(_capacity, s._capacity); 83. } 84. 85. //const string对象遍历,只读 86. char& operator[](size_t i) const//char要加&,如果不加,那么return就是传值返回,返回的是_str[i]的拷贝,临时对象,而临时对象具有常性,不能被修改,只能读 87. //如果要对_str进行修改的话,char要加&,用传引用返回 88. { 89. assert(i < _size); 90. return _str[i]; 91. } 92. 93. //非const string对象遍历,可读可写 94. char& operator[](size_t i) 95. { 96. assert(i < _size); 97. return _str[i]; 98. } 99. 100. //求string对象大小 101. size_t size() const 102. { 103. return strlen(_str); 104. } 105. 106. //获取c形式字符串,将 const string* 类型 转化为 const char* 类型 107. const char* c_str() 108. { 109. return _str; 110. } 111. 112. //开空间,扩展_capacity,_size不变 113. void reserve(size_t n) 114. { 115. //1.申请新空间 116. //2.拷贝字符串 117. //3.释放旧空间 118. //4.指向新空间 119. //5.更新容量 120. if (n > _capacity) 121. { 122. char* tmp = new char[n + 1];//1.申请新空间,多开的那一个空间,存放\0 123. strncpy(tmp, _str, _size + 1);//2.将字符串包含\0在内都拷贝到新空间 124. delete[] _str;//3.释放旧空间 125. 126. _str = tmp;//4.指向新空间 127. _capacity = n;//5.更新容量 128. } 129. } 130. 131. //开空间+初始化,扩展_capacity,_size也要修改 132. void resize(size_t n, char val = '\0')//如果没有显式给出val,val为\0 133. { 134. //1.当n<_size,无需增容 135. if (n < _size) 136. { 137. _size = n;//直接更新_size 138. _str[_size] = '\0';//将_size位置置为\0 139. } 140. //2.当n>_size,分两种情况 141. //(1)_size < n <_capacity,无需增容,直接将_size到n位置置为val 142. //(2)n >_capacity,需增容,并将_size到n位置置为val 143. //这两种情况只有是否增容的区别,其他没有区别 144. else 145. { 146. if (n > _capacity) 147. { 148. reserve(n); 149. } 150. 151. for (size_t i = _size; i < n; i++) 152. { 153. _str[i] = val; 154. } 155. 156. _str[n] = '\0';//将n的位置置\0 157. _size = n;//更新_size 158. } 159. } 160. 161. //尾插一个字符 162. void push_back(char ch) 163. { 164. //字符个数已经达到容量时,重新开空间 165. if (_size == _capacity) 166. { 167. reserve(_capacity == 0 ? 4 : _capacity * 2);//如果是第一次开空间,就开4个字节,如果不是第一次开空间,就开成原来的2倍容量 168. } 169. 170. _str[_size] = ch;//将字符ch缀到字符串末尾 171. _str[_size + 1] = '\0';//字符ch后面加\0,表明字符串结束 172. _size++;//字符串大小+1 173. } 174. 175. //追加字符串 176. void append(const char* str) 177. { 178. size_t len = _size + strlen(str);//函数执行完毕后字符串的大小 179. 180. if (len > _capacity) 181. { 182. reserve(len);//空间不够,增容 183. } 184. 185. strcpy(_str + _size, str);//将str内容拷贝至_str末尾 186. _size = len;//更新_size 187. } 188. 189. //+= 1个字符 190. string& operator+= (char c) 191. { 192. push_back(c);//尾插字符 193. return *this; 194. } 195. 196. //+= 字符串 197. string& operator+= (const char* str) 198. { 199. append(str);//拼接字符串 200. return *this; 201. } 202. 203. //insert插入一个字符 204. string& insert(size_t pos, const char ch) 205. { 206. assert(pos <= _size);//pos必须小于等于字符串长度 207. 208. if (_size == _capacity) 209. { 210. reserve(2 * _capacity);//如果满了就增容 211. } 212. 213. size_t end = _size + 1; 214. while (end > pos)//将pos至_size之间的字符依次向后挪动一个位置,来腾出pos位置 215. { 216. _str[end] = _str[end - 1]; 217. end--;//end为0时,因此end类型应为int,那么end--就为-1,pos为无符号数,不满足循环条件,退出循环 218. //但如果end类型为size_t,那么end为0时,end--就为-1,在内存中存储为无符号数(-1补码全1),满足循环条件,永远无法退出循环 219. } 220. 221. _str[pos] = ch;//pos位置插入ch 222. _size++;//更新_size 223. 224. return *this; 225. } 226. 227. //erase 删除字符 228. string& erase(size_t pos, size_t len = npos) 229. { 230. assert(pos < _size); 231. 232. size_t leftLen = _size - pos;//从pos位置到字符串结束的字符长度 233. if (leftLen < len)//1.pos之后的字符全部删掉 234. { 235. _str[pos] = '\0'; 236. _size = pos; 237. } 238. else//2.pos之后的字符删掉一部分,没删完的部分要挪到pos位置 239. { 240. strcpy(_str + pos, _str + pos + len); 241. _size -= len; 242. } 243. return *this; 244. } 245. 246. //查找字符 247. size_t find(char ch, size_t pos = 0) 248. { 249. assert(pos < _size); 250. for (size_t i = 0; i < _size; i++) 251. { 252. if (_str[i] == ch) 253. { 254. return i;//找到就返回字符所在位置 255. } 256. } 257. return npos;//没找到就返回-1 258. } 259. 260. //查找字符串(子串) 261. size_t find(const char* str, size_t pos = 0) 262. { 263. assert(pos < _size); 264. const char* ret = strstr(_str + pos, str); 265. 266. if (ret) 267. { 268. return ret - _str;//计算str与_str的偏移量,即str在_str中的下标 269. } 270. else 271. { 272. return npos;//没找到就返回-1 273. } 274. } 275. 276. 277. //字符串比较,只需要实现>和== 278. bool operator>(const string& s) 279. { 280. return strcmp(_str, s._str) < 0; 281. } 282. 283. bool operator==(const string& s) 284. { 285. return strcmp(_str, s._str) == 0; 286. } 287. 288. // 其它4个运算符都可以对>和==这两个运算符进行重载 289. bool operator>=(const string& s) 290. { 291. return (*this > s) || (*this == s); 292. } 293. 294. bool operator<(const string& s) 295. { 296. return !(*this >= s); 297. } 298. 299. bool operator<=(const string& s) 300. { 301. return !(*this > s); 302. } 303. 304. bool operator!=(const string& s) 305. { 306. return !(*this == s); 307. } 308. 309. void clear() 310. { 311. _size = 0; 312. _str[0] = '\0'; 313. } 314. 315. private: 316. char* _str; 317. size_t _size; 318. size_t _capacity; 319. 320. static const size_t npos; 321. }; 322. 323. const size_t string::npos = -1;//npos初始化 324. 325. //输出重载<<让string直接输出打印,像内置类型一样,用范围for就可以实现 326. ostream& operator<<(ostream& out, const string& s) 327. { 328. for (auto ch: s) 329. { 330. out << s; 331. } 332. return out; 333. } 334. 335. //输入重载>>让string直接输入,像内置类型一样;从标准输入流读取字符,遇到' '或'\0'就停止 336. istream& operator>>(istream& in, string& s) 337. { 338. s.clear(); 339. char ch; 340. ch = in.get(); 341. while (ch != ' ' && ch != '\0') 342. { 343. s += ch; 344. ch = in.get(); 345. } 346. return in; 347. } 348. 349. void f(const string& s) 350. { 351. for (size_t i = 0; i < s.size(); i++) 352. { 353. cout << s[i] << " "; 354. } 355. cout << endl; 356. } 357. 358. void test_string1() 359. { 360. string s1("hello world"); 361. string s2(s1); 362. 363. string s3("cplusplus.com"); 364. std::swap(s1, s3);//调用库里的swap函数,3次深拷贝,再把原来的空间释放掉,再开一样大的空间,代价大 365. s1.swap(s3);//调用delia::string类里面的成员函数swap,仅仅只是成员变量的交换,s3指向了s1的空间,s1指向了s3的空间 366. 367. string s4 = s1.c_str(); 368. s1[2] = 'w'; 369. f(s1);//h e w l o w o r l d 370. 371. for (size_t i = 0; i < s1.size(); i++) 372. { 373. cout << s1[i] << " "; 374. } 375. cout << endl;//h e w l o w o r l d 376. } 377. 378. void test_string2() 379. { 380. string s1("hello world"); 381. 382. //使用迭代器遍历 383. string::iterator it = s1.begin(); 384. while (it != s1.end()) 385. { 386. cout << *it << " "; 387. it++; 388. } 389. cout << endl;//h e l l o w o r l d 390. 391. //使用范围for遍历,范围for会被编译器替换成迭代器形式,如果把17-26行屏蔽,会发现编译不过 392. for (auto ch : s1) 393. { 394. cout << *it << " "; 395. } 396. cout << endl;//h e l l o w o r l d 397. } 398. 399. void test_string3() 400. { 401. string s1("hello world"); 402. s1 += "abc"; 403. for (auto& ch : s1) 404. { 405. cout << ch << " "; 406. } 407. cout << endl;//h e l l o w o r l d a b c 408. 409. s1 += "xyz"; 410. for (auto& ch : s1) 411. { 412. cout << ch << " "; 413. } 414. cout << endl;//h e l l o w o r l d a b c x y z 415. } 416. 417. void test_string4() 418. { 419. string s1("hello world"); 420. s1 += '#'; 421. for (auto ch : s1) 422. { 423. cout << ch << " "; 424. } 425. cout << endl;//hello world# 426. 427. s1 += "xyz"; 428. for (auto ch : s1) 429. { 430. cout << ch << " "; 431. } 432. cout << endl;//hello world#xyz 433. 434. s1 += "!!!!!!!!!!!!!!!!!!!!"; 435. for (auto ch : s1) 436. { 437. cout << ch << " "; 438. } 439. cout << endl;//hello world#xyz!!!!!!!!!!!!!!!!!!!! 440. } 441. 442. void test_string5() 443. { 444. string s1("hello world"); 445. s1.resize(3); 446. cout << s1.c_str() << endl;//hel 447. } 448. 449. void test_string6() 450. { 451. string s1("hello"); 452. s1.resize(6, 'a'); 453. cout << s1.c_str() << endl;//helloa 454. } 455. 456. void test_string7() 457. { 458. string s1("hello"); 459. s1.resize(9, 'o'); 460. cout << s1.c_str() << endl;//helloooo 461. } 462. 463. void test_string8() 464. { 465. string s1("hello"); 466. s1.insert(0, 'u'); 467. cout << s1.c_str() << endl;//uhello 468. } 469. 470. void test_string9() 471. { 472. string s1("hello"); 473. s1.erase(0, 2); 474. cout << s1.c_str() << endl;//llo 475. 476. string s2("world"); 477. s2.erase(1);//如果不给len,那么npos=-1,无符号数为4294967295,删除从位置为1-4294967295的字符,把后面的字符全部删除了 478. cout << s2.c_str() << endl;//w 479. } 480. 481. 482. void test_string10() 483. { 484. string s1("hello world"); 485. cout << s1.find("rl",6) << endl;//8,从s1中的第6个位置开始查找"rl"字符串 486. } 487. 488. void test_string11() 489. { 490. std::string s("hello"); 491. cin >> s; 492. cout << s << endl; 493. } 494. } 495. 496. 497. int main() 498. { 499. //delia::test_string1(); 500. //delia::test_string2(); 501. //delia::test_string3(); 502. //delia::test_string4(); 503. //delia::test_string5(); 504. //delia::test_string6(); 505. //delia::test_string7(); 506. //delia::test_string8(); 507. //delia::test_string9(); 508. //delia::test_string10(); 509. delia::test_string11(); 510. return 0; 511. }
二、在线OJ题
替换空格 OJ链接
分析:(1)如果使用string的replace或erase+insert都要挪动数据,效率低
(2)考虑直接用新串,用新串需要分配多大空间?一个空格占1个字节,‘%20’占3个字节,替换后,每个空格都比原来多两个字节,新串空间应为:原串长度+2*空格数
1. class Solution { 2. public: 3. string replaceSpace(string s) { 4. 5. //1.计算空格数 6. size_t spaceCount = 0; 7. for(auto ch:s) 8. { 9. if(ch == ' ') 10. { 11. spaceCount++; 12. } 13. } 14. 15. //2.声明新串 16. string ret; 17. 18. //3.为新串分配空间:从空格变为%20,长度增加2 19. ret.reserve(s.size()+2*spaceCount); 20. 21. //4.将空格替换为%20 22. for(auto ch:s) 23. { 24. if(ch != ' ') 25. { 26. ret += ch; 27. } 28. else 29. { 30. ret += "%20"; 31. } 32. } 33. 34. return ret; 35. } 36. };