C++STL与字符串探秘
目录
一、STL介绍
1.1 什么是STL
1.2 STL的6大组件编辑
二、string介绍
2.1 string的简单介绍
2.2 string接口说明
三、string的模拟实现
迭代器的指针实现
构造函数初始版和合并版
返回私有成员c_str实现
析构函数
swap的实现
拷贝函数(传统写法和现代写法)
string赋值运算符重载
size
下标引用(无const、const版本)
reserve扩容
push_back尾插字符
append尾插字符串
类尾插字符运算符重载
类尾插字符串运算符重载
empty判空
pop_back尾删
insert任意插入字符(字符串)
erase删除某段字符/字符串
find查询字符/字符串
substr模拟
比较大小(运算符重载)
clear清理
<<流插入
>>流提取(浪费空间版和plus版)
getline默认读取一行字符
一、STL介绍
1.1 什么是STL
STL C++标准库的重要组成部分,是一个包罗数据结构与算法的软件框架。
1.2 STL的6大组件
二、string介绍
2.1 string的简单介绍
一般汉字是占两个字节,偶尔的偏僻字会占三个字节。
学习string因为它里面能非常好的表示utf-8,而utf-8是变长编码,能过兼容ascii编码(老美的文字),string里面就是差不多字符顺序表。
string 遍历+修改方法
1.下标+[]调用运算符重载(有两个运算符重载一个是针对于非常量版本,还有一个是常量版本,编译器会根据string的实例化对象来看是调用非常量版本和常量版本,在这里常量版本虽说不能修改内部的值,但是他可以读取内部数据并保护数据不被篡改)
2.迭代器
正向迭代器
迭代器的意义:1.统一类似的方式遍历修改容器2.算法脱离具体底层结构并和底层结构解耦(不同数据结构的同样一个算法都会不太一样,但是有了迭代器那就用法都一样了,算法独立模块实现针对多个容器的处理如顺序表,链表这些容器)。
3.范围for C++11标准里面
前言:auto是自动判断类型,但是遇到引用类型的,那就需要自己加入&引用(指针也是,尽量用自己写*,虽然不写也可以识别地址,需要加取地址符号,而使用*就不用写取地址了,方便代码的可读性)。
范围for使用作用是1.自动取容器数据赋值给auto变量(自动取容器数据)。2.自动判断结束(不用写结束条件)。3.自动迭代(不需要自己对变量进行++)
它auto变量是通过迭代器拷贝给auto的值,这也导致无法修改auto变量的值,不过可以通过使用引用&把迭代器区别民进行修改容器里面值。
迭代器还有反向迭代器,就是从后往前迭代,也类似和指针一样,但是这里还是++,只是反过来加加罢了。
迭代器有4种,一种是非const正向迭代器,一种是非const反向迭代器,一种是const正向迭代器,一种是const反向迭代器。const迭代器大体分为const_iterator begin()const和iterator begin(),前者就是用于修饰指针指向的内容不能修改指向的内容,后者就是没有限制。反向迭代器就需要reverse_iterator他们两合体,其他const和非const不变直接加即可,reverse_iterator替代iterator。对于const的迭代器一般用cbegin cend区分,也可以不这么写,就正常写begin和end。
2.2 string接口说明
string内部计算的大小都不包含'\0'(标准库规定)
capacity是空间大小(会自动扩容先是2倍然后是1.5倍扩容)
clear是消除string的字符串(这不是销毁,而是消除里面的数据,开辟多空间没销毁)
empty判空
shrink_to_fit是缩容(原理就是时间换空间,重新开辟空间,然后释放另一个大空间,再让指针指向开辟的空间)
reserve是扩容接口(可能会大于扩容大小,因为有些会考虑内存对齐这些)如果知道你需要很多空间,就可以使用reserve进行提前扩容,避免后续2倍或1.5倍扩容,可以提高效率。
resize 针对于伸缩string里面size的大小,resize比内部字符串个数大,则会用空字符“\0”补充(默认),如果resize比内部字符个数小那就会删除从resize规定的大小开始后面的数据。
operator[]可以下标访问和修改(数组是抽查数组下标越界,而operator是函数,一般内部是断言,很严谨,可以检查出下标访问越界)。
at和operator[]进行下标访问和修改,但是at是抛异常,不像operator[]内部是断言,在Release版本下断言会忽略。
append是string追加字符串的一个接口。
operator+=一般追加字符串是比较好的一个接口。
push_back是一个追加一个字母的。
assign替换原来的字符串的。
insert用于在某个位置插入字符串或字符(谨慎使用,底层是数组挪动,效率会低下)
erase用于某个位置的消除(谨慎使用,同理也是数组挪动)。
replace用于替换字符串为一个字符或字符串(效率也低)。
getline用于读取一行,还可以读取指定结束字符。
更多接口可以参考string - C++ Reference
返回const char*类型的必须要考虑到解引用,在使用cout过程中,编译器会自动打印字符串(而不是地址),这里地址在打印字符串过程中就会涉及到空指针解引用问题(所以我们这里在对const char* 不要用空指针初始化)。
三、string的模拟实现
-
迭代器的指针实现
string.cpp文件:
//迭代器实现string::iterator string::begin(){return _str;}string::iterator string::end(){return _str + _size;}string::const_iterator string::begin()const{return _str;}string::const_iterator string::end()const{return _str + _size;}
test.c文件:
void test()
{string s1("hellow world");string::iterator it1 = s1.begin();while (it1 != s1.end()){//可修改cout << ++(*it1);++it1;}const string s2("hellow world");string::const_iterator it2 = s2.begin();while (it2 != s2.end()){//不可修改内部是const string* const thiscout << (*it1);++*it1;}
}
-
构造函数初始版和合并版
string.cpp文件 初始化版
//基础版
//string();
//string(const char* str);
//基础版string::string():_str(new char[1]{'\0'}), _size(0), _capacity(0){}string::string(const char* str){_size = _capacity = strlen(str);_str = new char[_capacity + 1];//strcpy(_str, str);//一般不使用这个,会有隐藏bug存在(遇到\0就停止,不能自己控制)memcpy(_str, str, _size + 1);}
string.cpp文件 合并版
//string();构造合并版
string(const char* str = " ");
//string();构造(合并版)string::string(const char* str){//不一定用初始化构造_capacity = _size = strlen(str);//strlen使用越少越好,这里是在编译过程中计算大小_str = new char[_capacity + 1];//strcpy(_str, str);memcpy(_str, str, _size + 1);}
这里用到了我之前学到的知识缺省值,这个缺省值可以应对基础版的第一个构造,可以避免代码的冗余。
-
返回私有成员c_str实现
//返回_str私有成员,便于调用const char* string::c_str()const{return _str;}
这个意义就是为了和c语言兼容,可以用c的接口进行处理一些情况,比如在文件函数中吧字符串插入的文件这是c语言的接口,而c语言不支持string这个接口,使用了c_str就能与c语言兼容起来。
-
析构函数
//析构string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}
-
swap的实现
//修改一些std的算法库(这里不能深拷贝,这里只需要浅拷贝)void string::swap(string& s)//这是string类中的swap{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//string内部swap的模拟void swap(string& x, string& y){x.swap(y);//调用string的类中的浅拷贝交换}
这里设置2个swap,swap是非成员函数可以使得其他类也能使用,能够使得string类无缝衔接C++标准库的算法和数据结构
性能优化:避免不必要的深拷贝,提供 O(1) 复杂度的交换操作
接口一致性:提供与 STL 标准一致的交换接口
泛型编程支持:使 string 类能无缝融入 C++ 标准库生态系统
-
拷贝函数(传统写法和现代写法)
这里会用到上面的swap函数
//拷贝传统写法string::string(const string& s){_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];memcpy(_str, s._str, _size + 1);}//拷贝现代写法string::string(const string& s){//空手套白狼string tmp(s._str);//这里不能直接写s,在此之前还没写出拷贝构造swap(tmp);//调用string成员函数}
像第一种就是老实写法,很传统写法,相当于自己去申请空间,然后自己去拷贝(黑奴)
第二种就是资本家写法,交给别人创建空间吧别人的东西拷贝过来,然后交换对于的值,窃取别人劳动成功给this这个指针。(资本家)
-
string赋值运算符重载
////赋值运算符重载string& string::operator=(const string& s){//资本家,都不用想他们相同情况if(*this != s){string tmp(s);swap(tmp);}return *this;}//传统写法string& string::operator=(const string& s){if(*this != s){_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;memcpy(_str, s._str, _size + 1);}return *this;}
-
size
//返回size大小size_t string::size()const{return _size;//简单明了}
-
下标引用(无const、const版本)
//无const版本下标引用char& string::operator[](size_t i){//可读可修改return _str[i];}//const版本下标引用const char& string::operator[](size_t i)const{//只读限制被修改return _str[i];}
-
reserve扩容
//扩容void string::reserve(size_t n){//大部分都是异地扩容_capacity = n;char* str = new char[_capacity + 1];memcpy(str, _str, _size + 1);//将值拷贝过来delete[] _str;_str = str;}
一般正常的扩容,分为2种,一种是原地扩容,在原有的基础上进行扩容空间(这种很少),另一种就是异地扩容,这种扩容是因为原有基础上的空间不够扩容了,在其他地方寻找这么大的空间进行开辟扩容,这种较为常见。一般都是这种扩容较多。
-
push_back尾插字符
//push_back尾插字符void string::push_back(const char ch){//空间不够用if (_size == _capacity){_capacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(_capacity);}//空间够用_str[_size] = ch;++_size;_str[_size] = '\0';}
-
append尾插字符串
//append尾插字符串void string::append(const char* str){size_t len = strlen(str);//空间不够if ( _capacity <= len + _size)//可能有时候len+_size会小于_capacity{_capacity = 2 * _capacity >= len + _size ? 2 * _capacity : len + _size;reserve(_capacity);}//空间足够//strcat(_str, str);memcpy(_str + _size, str, len+1);//别忘了'\0'停止符号拷贝_size += len;}
-
类尾插字符运算符重载
根据上面的push_back函数可以直接调用上面的函数来写运算符重载的尾插字符
//尾插字符运算符重载string& string::operator+=(const char ch){push_back(ch);return *this;}
-
类尾插字符串运算符重载
这里也是同理,上面的append函数的实现,直接调用即可
//尾插字符串运算符重载string& string::operator+=(const char* str){append(str);return *this;}
-
empty判空
//判空bool string::empty()const{return _size == 0;}
-
pop_back尾删
//pop_back尾删void string::pop_back(){assert(!empty());--_size;_str[_size] = '\0';}
-
insert任意插入字符(字符串)
插入字符
//任意插字符string& string::insert(size_t pos,const char ch){assert(pos <= _size);if (_size == _capacity){_capacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(_capacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;}
插入字符串
//任意插入字符串string& string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);//空间不够if (len + _size >= _capacity){_capacity = 2 * _capacity <= len + _size ? 2 * _capacity : len + _size;reserve(_capacity);}size_t end = _size + len;while (end >= pos + len){_str[end] = _str[end - len];--end;}//空间够//memmove(_str + pos+len, _str+pos, _size - pos + 1);//可以用memmove代替while循环,这种效率较高memcpy(_str+pos, str, len + 1);//内存重叠不能使用memecpy_size += len;return *this;}
-
erase删除某段字符/字符串
//任意删某段字符string& string::erase(size_t pos,size_t len){assert(pos <= _size);//后面一段全部删除if (len == npos || pos + len >= _size){_str[_size] = '\0';_size = pos;return *this;}//中间某段size_t pos_i = pos + len;//pos_i交换需要删除的//while (pos_i <= _size)//{// _str[pos_i - len] = _str[pos_i];// ++pos_i;//}memmove(_str + pos, _str + pos_i,_size-pos_i+1);//这里不能用memcpy内存重叠的会导致未定义行为_size -= len;return *this;}
-
find查询字符/字符串
寻找字符
//寻找字符size_t string::find(const char ch, size_t pos)const{assert(pos <= _size);for (int i = pos; i < _size; i++)//从pos位置开始查询{if (_str[i] == ch){return i;}}return npos;}
寻找字符串
//寻找字符串size_t string::find(const char* str, size_t pos)const{assert(pos <= _size);const char* ret = strstr(_str+pos, str);//从pos位置开始查询if (ret == nullptr){return npos;}return ret - str;//指针相减就是对应的元素个数(这里元素个数刚好就是对应的下标)}
-
substr模拟
//substr的模拟string string::substr(size_t pos, size_t len)const{assert(pos <= _size);//当len+pos >= _sizeif (len == npos || len + pos >= _size){len = _size - pos;}//当len+pos < _sizestring tmp;tmp.reserve(len);memcpy(tmp._str, _str + pos, len + 1);//从pos位置开始计算len个存储在tmp中tmp._size = len;return tmp;}
-
比较大小(运算符重载)
//比较大小bool string::operator<(const string& s)const{return strcmp(_str, s._str) < 0;}bool string::operator<=(const string& s)const{return strcmp(_str,s._str) <= 0 ;}bool string::operator>(const string& s)const{return !operator<=(s);}bool string::operator>=(const string& s)const{return !operator<(s);}bool string::operator==(const string& s)const{return strcmp(_str, s._str) == 0;}bool string::operator!=(const string& s)const{return !operator==(s);}
-
clear清理
//clear消除字符void string::clear(){_str[0] = '\0';_size = 0;}
-
<<流插入
//流插入ostream& operator<<(ostream& out, const string& s){//pass这种,遇到\0不会读取//out << s.c_str();//return out;//循环遍历(确保每一个元素都被读取)for (int i = 0; i < s.size(); i++){cout << s[i];}return out;}
-
>>流提取(浪费空间版和plus版)
//流提取(浪费空间版)istream& operator>>(istream& in,string& s){//清除内部元素s.clear();//cin是不会读取空格和\n换行字符的char ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}//流提取(实效版)istream& operator>>(istream& in, string& s){//还是先清除内部元素s.clear();char ch = in.get();size_t i = 0;//用数组存储(差不多就是缓存,后面一次性扩容,避免频繁扩容和空间浪费)char buffer[128] = {0};while (ch != ' ' && ch != '\n'){buffer[i] = ch;++i;if (i == 127){buffer[i] = '\0';//避免刚好到i位置没停止符号而引起的乱码s += buffer;i = 0;}ch = in.get();}//考虑没到127,需要补充if (i > 0){buffer[i] = '\0';s += buffer;i = 0;}return in;}
-
getline默认读取一行字符
istream& getline(istream& in, string& s, const char delim){//同理需要清除内部元素s.clear();char ch = in.get();//和流提取类似char buffer[128];size_t i = 0;while (ch != delim)//默认读取一行{buffer[i] = ch;++i;if (i == 127){buffer[i] = '\0';s += buffer;i = 0;}ch = in.get();}if (i > 0){buffer[i] = '\0';s += buffer;i = 0;}return in;}
完整代码stirng.h
#pragma once
#include<iostream>
#include<string>using namespace std;
namespace bear
{class string{public://迭代器的指针实现typedef char* iterator;typedef const char* const_iterator;iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;//string();构造合并版string(const char* str = " ");//基础版//string();//string(const char* str);//提取私有字符串const char* c_str() const;//析构~string();//拷贝string(const string& s);//赋值string& operator=(const string& str);//赋值//string& operator=(string str);//size()的实现size_t size() const;//下标引用(无const)char& operator[](size_t i);//下标引用(有const)const char& operator[](size_t i) const;//扩容void reserve(size_t n);//尾插入字符void push_back(const char ch);//尾插入字符串void append(const char* str);//尾插字符(运算符重载)string& operator+=(const char ch);//尾插字符串(运算符重载)string& operator+=(const char* str);//判空bool empty()const;//尾删void pop_back();//任意插入字符string& insert(size_t pos, const char ch);//任意插入字符串string& insert(size_t pos, const char* str);//任意删某段字符串/字符string& erase(size_t pos = 0, size_t len = npos);//从pos位置开始查询第一个出现字符size_t find(const char ch, size_t pos = 0) const;//从pos位置开车查询第一个出现字符串size_t find(const char* str, size_t pos = 0) const;//从pos位置开始拷贝len个长度的stringstring substr(size_t pos, size_t len = npos) const;//比较大小bool operator<(const string& s) const;bool operator<=(const string& s) const;bool operator>(const string& s) const;bool operator>=(const string& s) const;bool operator==(const string& s) const;bool operator!=(const string& s) const;//清理void clear();//swapvoid swap(string& s);private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;public:static const size_t npos;};//流插入std::ostream& operator<<(std::ostream& out, string const& s);//流提取std::istream& operator>>(std::istream& in, string& s);//getline从str中读取一行或者读取到某个字符停止std::istream& getline(std::istream& in, string& s, const char delim = '\n');//swapvoid swap(string& x, string& y);
}
string.cpp
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
#include<assert.h>namespace bear
{const size_t string::npos = -1;//迭代器实现string::iterator string::begin(){return _str;}string::iterator string::end(){return _str + _size;}string::const_iterator string::begin()const{return _str;}string::const_iterator string::end()const{return _str + _size;}////基础版//string::string()// :_str(new char[1]{'\0'})// , _size(0)// , _capacity(0)//{// //}//string::string(const char* str)//{// _size = _capacity = strlen(str);// _str = new char[_capacity + 1];// //strcpy(_str, str);//一般不使用这个,会有隐藏bug存在(遇到\0就停止,不能自己控制)// memcpy(_str, str, _size + 1);//}//string();构造(合并版)string::string(const char* str){//不一定用初始化构造_capacity = _size = strlen(str);//strlen使用越少越好,这里是在编译过程中计算大小_str = new char[_capacity + 1];//strcpy(_str, str);memcpy(_str, str, _size + 1);}//返回_str私有成员,便于调用const char* string::c_str()const{return _str;}//析构string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}//修改一些std的算法库(这里不能深拷贝,这里只需要浅拷贝)void string::swap(string& s)//这是string类中的swap{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//string内部swap的模拟void swap(string& x, string& y){x.swap(y);//调用string的类中的浅拷贝交换}////拷贝传统写法//string::string(const string& s)//{// _size = s._size;// _capacity = s._capacity;// _str = new char[_capacity + 1];// memcpy(_str, s._str, _size + 1);//}//拷贝现代写法string::string(const string& s){//空手套白狼string tmp(s._str);//这里不能直接写s,在此之前还没写出拷贝构造swap(tmp);//调用string成员函数}////赋值运算符重载//string& string::operator=(const string& s)//{// //资本家,都不用想他们相同情况// if(*this != s)// {// string tmp(s);// swap(tmp);// }// return *this;//}//传统写法string& string::operator=(const string& s){if(*this != s){_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;memcpy(_str, s._str, _size + 1);}return *this;}//返回size大小size_t string::size()const{return _size;}//无const版本下标引用char& string::operator[](size_t i){return _str[i];}//const版本下标引用const char& string::operator[](size_t i)const{return _str[i];}//扩容void string::reserve(size_t n){//大部分都是异地扩容_capacity = n;char* str = new char[_capacity + 1];memcpy(str, _str, _size + 1);//将值拷贝过来delete[] _str;_str = str;}//push_back尾插字符void string::push_back(const char ch){//空间不够用if (_size == _capacity){_capacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(_capacity);}//空间够用_str[_size] = ch;++_size;_str[_size] = '\0';}//append尾插字符串void string::append(const char* str){size_t len = strlen(str);//空间不够if (_capacity <= len + _size){_capacity = 2 * _capacity >= len + _size ? 2 * _capacity : len + _size;reserve(_capacity);}//空间足够//strcat(_str, str);memcpy(_str + _size, str, len+1);//别忘了'\0'停止符号拷贝_size += len;}//尾插字符运算符重载string& string::operator+=(const char ch){push_back(ch);return *this;}//尾插字符串运算符重载string& string::operator+=(const char* str){append(str);return *this;}//判空bool string::empty()const{return _size == 0;}//pop_back尾删void string::pop_back(){assert(!empty());--_size;_str[_size] = '\0';}//任意插字符string& string::insert(size_t pos,const char ch){assert(pos <= _size);if (_size == _capacity){_capacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(_capacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;}//任意插入字符串string& string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);//空间不够if (len + _size >= _capacity){_capacity = 2 * _capacity <= len + _size ? 2 * _capacity : len + _size;reserve(_capacity);}size_t end = _size + len;while (end >= pos + len){_str[end] = _str[end - len];--end;}//空间够//memmove(_str + pos+len, _str+pos, _size - pos + 1);//可以用memmove代替while循环memcpy(_str+pos, str, len + 1);//内存重叠不能使用memecpy_size += len;return *this;}//任意删某段字符string& string::erase(size_t pos,size_t len){assert(pos <= _size);//后面一段全部删除if (len == npos || pos + len >= _size){_str[_size] = '\0';_size = pos;return *this;}//中间某段size_t pos_i = pos + len;//pos_i交换需要删除的//while (pos_i <= _size)//{// _str[pos_i - len] = _str[pos_i];// ++pos_i;//}memmove(_str + pos, _str + pos_i,_size-pos_i+1);//这里不能用memcpy内存重叠的会导致未定义行为_size -= len;return *this;}//寻找字符size_t string::find(const char ch, size_t pos)const{assert(pos <= _size);for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}//寻找字符串size_t string::find(const char* str, size_t pos)const{assert(pos <= _size);const char* ret = strstr(_str+pos, str);if (ret == nullptr){return npos;}return ret - str;//指针相减就是对应的元素个数(这里元素个数刚好就是对应的下标)}//substr的模拟string string::substr(size_t pos, size_t len)const{assert(pos <= _size);//当len+pos >= _sizeif (len == npos || len + pos >= _size){len = _size - pos;}//当len+pos < _sizestring tmp;tmp.reserve(len);memcpy(tmp._str, _str + pos, len + 1);//从pos位置开始计算len个存储在tmp中tmp._size = len;return tmp;}//比较大小bool string::operator<(const string& s)const{return strcmp(_str, s._str) < 0;}bool string::operator<=(const string& s)const{return strcmp(_str,s._str) <= 0 ;}bool string::operator>(const string& s)const{return !operator<=(s);}bool string::operator>=(const string& s)const{return !operator<(s);}bool string::operator==(const string& s)const{return strcmp(_str, s._str) == 0;}bool string::operator!=(const string& s)const{return !operator==(s);}//clear消除字符void string::clear(){_str[0] = '\0';_size = 0;}//流插入ostream& operator<<(ostream& out, const string& s){//pass这种,遇到\0不会读取//out << s.c_str();//return out;//循环遍历(确保每一个元素都被读取)for (int i = 0; i < s.size(); i++){out << s[i];}return out;}////流提取(浪费空间版)//istream& operator>>(istream& in,string& s)//{// //清除内部元素// s.clear();// //cin是不会读取空格和\n换行字符的// char ch = in.get();// while (ch != ' ' && ch != '\n')// {// s += ch;// ch = in.get();// }// return in;//}//流提取(实效版)istream& operator>>(istream& in, string& s){//还是先清除内部元素s.clear();char ch = in.get();size_t i = 0;//用数组存储(差不多就是缓存,后面一次性扩容,避免频繁扩容和空间浪费)char buffer[128] = {0};while (ch != ' ' && ch != '\n'){buffer[i] = ch;++i;if (i == 127){buffer[i] = '\0';//避免刚好到i位置没停止符号而引起的乱码s += buffer;i = 0;}ch = in.get();}//考虑没到127,需要补充if (i > 0){buffer[i] = '\0';s += buffer;i = 0;}return in;}istream& getline(istream& in, string& s, const char delim){//同理需要清除内部元素s.clear();char ch = in.get();//和流提取类似char buffer[128];size_t i = 0;while (ch != delim)//默认读取一行{buffer[i] = ch;++i;if (i == 127){buffer[i] = '\0';s += buffer;i = 0;}ch = in.get();}if (i > 0){buffer[i] = '\0';s += buffer;i = 0;}return in;}
}