C++ STL 文件读取和容器(转载自http://blog.sina.com.cn/s/blog_735f29100102uwwd.html)

来源:互联网 发布:java 文件存在判断 编辑:程序博客网 时间:2024/05/22 03:11

STL部分:

 IO对象无法复制或者赋值,所以io对象作为函数形参或返回值时只能使用指针或引用。

 

     iostream &Getio(iostream &io, fstream *fs){ ...} // 正确,参数和返回以引用或指针形式传递

     iostream Getio(iostream io){ ...}  // 错误,参数和返回以拷贝方式传递会发生复制和赋值操作


  • 用操纵符(endl, ends,flush)显式地刷新缓冲区:

cout<<"Hi!"<<endl; //插入换行符,并刷新缓冲区

cout<<"Hi!"<<ends; //插入空格字符null,并刷新缓冲区

cout<<"Hi!"<<flush; //仅仅刷新缓冲区,不插入任何其他字符



创建文件流对象时,我们可以提供文件名(可选)。如果提供了一个文件名,则open会自动隐式地被调用。

  ifstream in((“cpp-home.txt”); //构造一个ifstream对象 in 并打开给定文件。

如果我们定义了一个空的文件流,我们使用它的成员函数open() 将它与某一文件关联起来。

ofstream Out;
Out.open (“cpp-home.txt”, ios::out | ios::app | ios::binary) ;

这里ios代表某一种IO类型,比如fstream

ios::in为输入(读)而打开文件ios::out为输出(写)而打开文件ios::ate初始位置:文件尾ios::app所有输出附加在文件末尾ios::trunc如果文件已存在则先删除该文件ios::binary二进制方式
类参数的默认方式ofstreamios::out | ios::truncifstreamios::infstreamios::in | ios::out
比如若只以out模式打开,默认情况下自带trunc属性,若想跟在原来的文件后面添加内容的话一定要显式指定app模式。

 当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close()。

弄一个最简单的创建然后读取文件的程序:

#include 《fstream》

#include 《iostream》

using namespace std;


void Writefile();

void Readfile();


void main()

{

 Writefile();

 Readfile();

}


void Writefile()

{

   ofstream SaveFile("d:/cpp-home.txt");

   SaveFile<<"Hello World!";

   SaveFile.close();

}

void Readfile()

{

    ifstream OpenFile("d:/cpp-home.txt");

 

    char ch;

    while(OpenFile.get(ch))

    {

       cout<< ch;

    }

  cout<<endl;

    OpenFile.close();

}


改进:

void Writefile()

   ofstream SaveFile;

   SaveFile.open("c:/Temp/cpp-home.txt",ios::app);

   SaveFile<<"Hello World!"<<endl;

   SaveFile<<"Hello World again!"<<endl;

   SaveFile.close();

 

}


 Readfile()里面试试:

string str1;

while(getline(OpenFile, str1))

    {

cout<<str1;

 

    }

打印

Hello World!

然后再试试:

string str1,str2;

while(OpenFile>>str1>>str2)

    {

cout<<str1<<endl<<str2<<endl;

 

    }

打印

Hello 

World!



容器

标准STL序列容器:vector、string、deque和list,forward_list,array

vector和string都是可变大小的数组, 一般用于在尾部插入或删除元素。

list 双向链表

forward_list 单项链表

这两个插入很快,在各个位置都很快的插入或者删除,但是不支持随机访问,查找很慢

forward_list容器与list容器的主要设计区别是list保持内部唯一的一个链接到下一个元素,而后者则保持每个元素两个链接:一个指向下一个元素和一个前一个

array 是大小固定的数组,更安全。


为了定义容器对象首先要包含相关头文件,如#include#include#include


对迭代器++ (递增)从当前元素移动到下一个元素。

forward_list的迭代器不支持--操作。

容器初始化:

1. 定义一个已知长度的 vector :

vector< int > ivec( 10 );  //类似数组定义int ia[ 10 ];

vector< int > ivec( 10,0 );  10个0

花括号列表初始化vector< int > ivec{1,2,3,4,5,6};  //只支持C++11

 vector 初始化

 string str[]={"hello","world","this","find","gank","pink","that","when","how","cpp"};  

 vector《string》 A(str,str+10);          

//注意,尽量这样初始化,或者一个一个的pushback,不要 vector A={"hello","world",..};尽管这个在C++11中已经支持。但最好不要用(起码我试过VS2012都不支持。但VS2013支持)。vectors1="Wusiwen"; //不允许, Vector没有一个接受向量常量的构造函数。 string 是向量。


C++ <wbr>STL <wbr>文件读取和容器


顺序容器的一些操作:

http://www.cnblogs.com/ForFreeDom/archive/2012/04/26/2470971.html    

3.容器的 begin 和 end 操作:

c.begin()   返回一个迭代器,它指向容器 c 的第一个元素

c.end()     返回一个迭代器,它指向容器 c 的最后一个元素的下一位置

 

       4.在顺序容器中添加元素:在容器中添加元素时,系统是将元素值复制到容器里。类似地,使用一段元素初始化新容器时,新容器存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响,反之亦然。

c.push_back(t)              在容器 c 的尾部添加值为 t 的元素。返回 void 类型

c.push_front(t)             在容器 c 的前端添加值为 t 的元素。返回 void 类型     只适用于 list 和 deque 容器类型.

c.insert(p,t)               在迭代器 p 所指向的元素前面插入值为 t 的新元素。返回指向新添加元素的迭代器

c.insert(p,n,t)                     在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素。返回 void 类型

c.insert(p,b,e)             在迭代器 p 所指向的元素前面插入由迭代器 b 和 e 标记的范围内的元素,但不包括e本身,e可以是指向最后一个元素的下一个元素的迭代器。返回 void 类型

 

       5.任何 insert 或 push 操作都可能导致迭代器失效。当编写循环将元素插入到 vector 或 deque 容器中时,程序必须确保迭代器在每次循环后都得到更新。避免存储 end 操作返回的迭代器

 

          7.容器大小的操作:

c.size()       返回容器 c 中的元素个数。返回类型为 c::size_type

c.max_size()   返回容器 c 可容纳的最多元素个数,返回类型为 c::size_type

c.empty()      返回标记容器大小是否为 0 的布尔值

c.resize(n)    调整容器 c 的长度大小,使其能容纳 n 个元素,如果 n < c.size(),则删除多出来的元素;否则,添加采用值初始化的新元素

c.resize(n,t)  调整容器 c 的长度大小,使其能容纳 n 个元素。所有新添加的元素值都为 t

          注意:resize 操作可能会使迭代器失效。在 vector 或 deque 容器上做 resize 操作有可能会使其所有的迭代器都失效。对于所有的容器类型,如果 resize 操作压缩了容器,则指向已删除的元素迭代器失效。

 

         8.访问元素:

c.back()    返回容器 c 的最后一个元素的引用。如果 c 为空,则该操作未定义

c.front()   返回容器 c 的第一个元素的引用。如果 c 为空,则该操作未定义

c[n]        返回下标为 n 的元素的引用。如果 n <0 或 n >= c.size(),则该操作未定义。 只适用于 vector 和 deque 可随机访问的 容器

c.at(n)     返回下标为 n 的元素的引用。如果下标越界,则该操作未定义。只适用于 可随机访问的 容器

              注意:a.使用越界的下标,或调用空容器的 front 或 back 函数,都会导致程序出现严重的错误

                        c.如果给出的下标无效,at 函数将会抛出 out_of_range 异常.这是我们使用它的原因

 

       9.删除元素:

c.erase(p)    删除迭代器 p 所指向的元素.返回一个迭代器,它指向被删除元素后面的元素。如果 p 指向容器内的最后一个元素,则返回的迭代器指向容器的超出末端的下一位置。如果 p 本身就是指向超出末端的下一位置的迭代器,则该函数未定义

c.erase(b,e)  删除迭代器 b 和 e 所标记的范围内所有的元素,但不会删除e.回一个迭代器,它指向被删除元素段后面的元素。实际上返回的就是e.果 e 本身就是指向超出末端的下一位置的迭代器,则返回的迭代器也指向容器的超出末端的下一位置

c.clear()     删除容器 c 内的所有元素。返回 void

c.pop_back()  删除容器 c 的最后一个元素。返回 void。如果 c 为空容器,则该函数未定义

c.pop_front()  删除容器 c 的第一个元素。返回 void。如果 c 为空容器,则该函数未定义.只适用于 list 或 deque 容器

             注意:erase、pop_front 和 pop_back 函数使指向被删除元素的所有迭代器失效。对于 vector 容器,指向删除点后面的元素的迭代器通常也会失效。而对于 deque 容器,如果删除时不包含第一个元素或最后一个元素,那么该 deque 容器相关的所有迭代器都会失效。

 

     10.赋值与 swap:

c1 = c2        删除容器 c1 的所有元素,然后将 c2 的元素复制给 c1c1 和 c2 的类型(包括容器类型和元素类型)必须相同

c1.swap(c2)    交换内容:调用完该函数后,c1 中存放的是 c2 原来的元素,c2 中存放的则是 c1 原来的元素。c1 和 c2 的类型必须相同。该函数的执行速度通常要比将 c2 复制到 c1 的操作快


c.assign(b,e)  重新设置 c 的元素:将迭代器 b 和 e 标记的范围内所有的元素复制到 c 中。b 和 e 必须不是指向 c 中元素的迭代器。

例如:c1.assign(c2.cbegin(),c2.cend()) ;

c.assign(n,t)  将容器 c 重新设置为存储 n 个值为 t 的元素

            注意:A.赋值和 assign 操作使左操作数容器的所有迭代器失效。swap 操作则不会使迭代器失效。完成 swap 操作后,尽管被交换的元素已经存放在另一容器中,但迭代器仍然指向相同的元素。

 

                     B.assign 操作首先删除容器中所有的元素,然后将其参数所指定的新元素插入到该容器中。与复制容器元素的构造函数一样,如果两个容器类型相同,其元素类型也相同,就可以使用赋值操作符(=)将一个容器赋值给另一个容器。如果在不同(或相同)类型的容器内,元素类型不相同但是相互兼容,则其赋值运算必须使用 assign 函数。例如,可通过 assign 操作实现将 vector 容器中一段 char* 类型的元素赋给 string 类型 list 容器。

 

                     C.由于 assign 操作首先删除容器中原来存储的所有元素,因此,传递给 assign 函数的迭代器不能指向调用该函数的容器内的元素。带有一对迭代器参数的 assign 操作允许我们将一个容器的元素赋给另一个不同类型的容器。

 

D.swap 操作实现交换两个容器内所有元素的功能。要交换的容器的类型必须匹配:操作数必须是相同类型的容器,而且所存储的元素类型也必须相同。调用了 swap 函数后,右操作数原来存储的元素被存放在左操作数中,反之亦然。

     vector svec1(10); // vector with 10 elements     vector svec2(24); // vector with 24 elements

vector::iterator itor=svec1.begin(); 
vector::iterator itor2=svec1.end();

    svec1.swap(svec2);      //执行 swap 后,容器 svec1 中存储 24 个 string 类型的元素,而 svec2 则存储 10 个元素。

 

          //执行swap后,itor,itor2不失效,而是分别指向svec2中对应的元

然而,尽量使用非成员函数版本的swap:    swap(c1,c2);这个对泛型编程很有用。



额外的string操作

http://blog.csdn.net/stephen1315/article/details/7444318

构造string的其他方法

</pre><pre code_snippet_id="390774" snippet_file_name="blog_20140613_2_4395731" name="code" style="white-space: normal; margin-top: 0px; margin-bottom: 0px; padding: 0px;">const char *cp="Hello World!!!"; //以空字符‘\0’结束的数组
string s(cp); //拷贝cp中的字符,直到遇到空字符;
</pre><pre code_snippet_id="390774" snippet_file_name="blog_20140613_1_1462365" name="code" style="white-space: normal; margin-top: 0px; margin-bottom: 0px; padding: 0px;">//n, len2, pos2 都是无符号值 <wbr></wbr>
 <wbr>string s(s2, pos2, len2) //s是s2从pos2开始Len2个字符</wbr>

string s7(s, 6, 20); //正确,只拷贝到s1末尾;s7=="World!!!" <wbr></wbr>

substr操作

这个操作返回一个string ,他是原始的string 的一部分或者全部拷贝

string s("hello world"); <wbr></wbr>
string s4=s.substr(6, 11); //s4=world <wbr></wbr>
</pre><h2 style="margin: 0px; padding: 0px; border: 0px; list-style: none;">改变string的其他方法</h2></div><div style="margin: 0px; padding: 0px; font-family: Tahoma; line-height: 24px;">string容器也支持assign(赋值),insert(插入),erase(删除),运算</div><div style="margin: 0px; padding: 0px; font-family: Tahoma; line-height: 24px;"><br style="margin: 0px; padding: 0px;" /></div><h3 style="margin: 0px; padding: 0px; border: 0px; list-style: none; font-family: Tahoma; line-height: 24px;">append和replace函数</h3><div style="margin: 0px; padding: 0px; font-family: Tahoma; line-height: 24px;">append就是在string末尾进行插入的一种简单的形式</div><p style="margin-top: 0px; margin-bottom: 8px; padding-top: 0px; padding-bottom: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal;"> <wbr></wbr></p><div style="margin: 0px; padding: 0px; font-family: Tahoma; line-height: 24px;"><pre code_snippet_id="390774" snippet_file_name="blog_20140613_4_4505198" name="code" style="white-space: normal; margin-top: 0px; margin-bottom: 0px; padding: 0px;">string s("C++ Primer"), s2=s; //将s和s2初始化为C++ Primer
 <wbr></wbr>
s.insert(s.size(), "4th Ed."); //s==“C++ Primer 4th Ed。”
s2.append("4th Ed."); //和上面的等价 <wbr></wbr>
</pre><pre code_snippet_id="390774" snippet_file_name="blog_20140613_4_4505198" name="code" style="white-space: normal; margin-top: 0px; margin-bottom: 0px; padding: 0px;">s.erase(11, 3); //s=="C++ Primer Ed." <wbr></wbr>
s.insert(11, "5th"); //s=="C++ Primer 5th Ed." //从位置11开始,删除3个字符并插入"5th" s2.replace(11, 3, "Fifth"); //s=="C++ Primer Fifth Ed."


string搜索操作 : 每个搜索操作都返回一个string::size_type 值,表示匹配发生位置的下标(大小写敏感)

最简单的是find函数

string name("AnnBelle");

auto pos1 = name.find("Anna"); //"Anna"的第一次出现的位置为0

1 s.find(args); //在s中args的第一次出现的位置 2 s.rfind(args); //最后一次出现 3 s.find_first_of(args);//在字符串s查找第一个与args中的任意某个字符匹配的字符的位置

string name("A1n2");

string Num("1234567890");

 

auto pos1 = name.find_first_of(Num);//返回1,即name 中的第一个数字下标


s.compare(s2); //比较s和s2 //compare的返回值:>0 s大; <0 s小; =0 二者相等


容器适配器: 

(为什么要搞一个这个?想想设计模式中的适配器模式。原理跟这个差不多吧。STL帮你实现了一个这个适配器,性能还不错,直接用就行了)

3种STL容器适配器是 

栈、队列和优先级队列。

stack适配器所关联的容器可以是vector list deque中任意一种。

queue适配器则要求关联操作必须提供push_front操作,因此不能是vector


使用适配器时,必须包含相关的头文件:
#include         #include

适配器的初始化
deque deq; //假定deq是一个deque
stack stk(deq);  //将deq中的元素复制到stk中

默认的stack和queue都基于deque容器实现,

在创建适配器时,通过将一个顺序容器指定为适配器的第二个类型实参,可覆盖其关联的基础容器类型:
stack< string, vector《string》 > str_stk; //在vector上实现的空栈
stack< string, vector《string》 > str_stk(svec);//在vector上实现的栈,初始化时保存svec的拷贝

栈适配器

s.empty()                如果栈为空,返回true,否则返回false
s.size()                    返回栈中元素的个数
s.pop()                    删除栈顶元素,但不返回其值
s.top()                    返回栈顶元素,但不删除其值
s.push(item)           在栈顶压入新元素


队列和优先级队列

q.empty()         如果对列为空,返回true,否则返回false
q.size()             返回队列中元素的个数
q.pop()             删除队列首元素,但不返回其值
q.front()           返回队列首元素的值,但不删除该元素   
q.back()           返回队尾元素的值,而不删除该元素       
q.push(item)   对于queue,在队尾压入一个新元素


C++标准模板库中,栈类(stack)的成员函数stack::push()在栈顶端添加元素,stackpop()从非空栈的栈顶端中删除一个元素,stackempty()判断栈是否为空,stack::top()返回非空栈的栈顶元素,stack::size()返回栈中元素的个数,构造一个int类型的栈,然后对这个栈调用以上几个函数,体会栈这种数据结构的特点及其成员函数的用法。

C++ <wbr>STL <wbr>文件读取和容器

C++中的动态内存与智能指针

使用new和delete来管理动态内存常出的一些错误:

1.忘记delete,即导致了“内存泄漏”,

 

2.野指针。在对象已经被释放掉之后,指针会置为空。这时候我们再次使用,会产生使用非法内存的指针。注意delete并不是要删除指针,而是释放指针所指的内存。

不过如果我们需要保留指针,可以在delete以后将nullptr赋予指针,这样指针就不指向任何对象了,如下代码:

1
2
3
4
auto p(newauto 42);
auto q = p;
deletep;
p = nullptr;

所以C++标准库中的智能指针很好的解决了这些问题。


shared_ptr是一个类,它跟vector类似,也是一个模板。

shared_ptr<string> p1;  //可以指向string的shared_ptr


经常使用make_shared函数来分配和使用这种动态内存:

shared_ptr<int> p2 = make_shared<int>(42);  //指向一个值为42 的int的shared_ptr

shared_ptr <string>p3 = make_shared<string>(4,'9');  //指向一个值为9999 的string的shared_ptr

或者经常使用auto方法 auto p2 = make_shared<int>(42); 

 

每次创建新对象时,初始化指针并将引用计数置为1;
auto p= make_shared<int>(42);     //p指向的对象只有一个,q的计数为1
    当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
无论何时,只要对某一个shared_ptr拷贝,比如
用它来初始化另一个shared_ptr,
或者用它作为参数传递给一个函数
或者作为函数的返回值,
它的计数都+1
auto q(p)  //两个shared_ptr指针q和p指向相同的对象,p计数+1


    对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;这是因此左侧的指针指向了右侧指针所指向的对象,因此右指针所指向的对象的引用计数+1;

  auto p= make_shared<int>(42);     

 auto r= make_shared<int>(12);     

r=q;

r的计数-1,q的计数+1

     (如果引用计数减至0,则自动销毁所管理的对象)。


如果要在多个对象间共享数据,我们可以使用动态内存。这可以 结合shared_ptr指针来实现。

在这种情况下、 构造函数 如果函数的实参数量未知,但类型都相同,我们可以使用标准库中的initializer_list  (定义在同名头文件中)


当动态对象不再被使用时,shared_ptr还会自动释放动态对象。比如

int main()

{

...

void f1()

{

 auto r= make_shared<int>(12);  

} //r离开作用域,它指向的内存会自动被释放。


...

}

在子函数f1中定义了一个局部变量的智能指针r。当r离开作用域,计数-1,它指向的内存会自动被释放。

当然了,如果在这期间它的计数增加了,离开作用域以后计数不为0那就不销毁了。


若某一个智能指针,

p.reset() 和p.unique()经常联合使用,来控制多个shared_ptr共享的对象。


通过shared_ptr可以让多个智能指针对象同时拥有某一块内存的访问权.unique_ptr同时只能有一个智能指针对象指向某块内存.


unique_ptr无法进行复制构造与赋值操作,但在函数中作为返回值却可以用.

http://blog.csdn.net/pi9nc/article/details/12227887


关联容器:
 关联容器与顺序容器的本质区别在于:关联容器是通过键(key)存储和读取元素的,而顺序容器则通过元素在容器中的位置顺序存储和访问元素。 

首先要了解名为pair 的标准库类型
 pair模板类用来绑定两个对象为一个新的对象,该类型在头文件中定义。pair类型提供的操作如下表:
pair《T1,T2》 p1; 创建一个空的pair对象,它的两个元素分别是T1和T2类型,采用值初始化 
pair《T1,T2》 p1(v1, v2); 创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2
 
make_pair(v1, v2) 以v1和v2值创建一个新的pair对象,其元素类型分别是v1和v2的类型 



关联容器支持通过键来高效地查找和读取元素,两个基本的关联容器是map和set。
map的元素是“键-值”对的二元组形式:键用作元素在map中的索引,而值则表示所存储和读取的数据。
set仅包含一个键,没有值。并有效地支持关于某个键是否存在的查询。
set和map类型的对象所包含的元素都具有不同的键。
如果需要一个键对应多个实例,则需要使用multimap或multiset类型。这两种类型允许多个元素拥有相同键。

map           关联数组:元素通过键来存储和读取 
multimap   支持同一个键多次出现的map类型
上面两个定义在map头文件中

set             大小可变的集合,支持通过键实现的快速读取  
multiset      支持同一个键多次出现的set类型
上面两个定义在set 头文件中


map的元素类型为pair类型,且键成员不可修改。其它类型别名与顺序容器一样。
map对象的定义
map 《k,v》 m; 创建一个名为m的空map对象,其键和值的类型分别为K和V 
map《k,v》 m(m2); 创建m2的副本m,m与m2必须有相同的键类型和值类型 


比如定义一个map并初始化:

map 《string,int》m = { {"zhao",1}, {"qian",2} , {"sun",3} , {"li",4} };

查找元素
m.count(k) 返回m中键为k的出现次数(0或1) 
m.find(k) 如果容器中存在键为k的元素,则返回指向该元素的迭代器。


map 类定义的类型

map::key_type 

在 map 容器中,用做索引的键的类型

map::mapped_type

在 map 容器中,键所关联的值的类型

map::value_type

一个 pair 类型,它的 first 元素具有 const map::key_type 类型,而 second 元素则为 map::mapped_type 类型


map::iterator it            迭代器

m.erase(k)

删除 中键为 的元素。返回 size_type 类型的值,表示删除的元素个数

m.erase(p)

从 中删除迭代器 所指向的元素。p 必须指向 中确实存在的元素,而且不能等于 m.end()。返回 void



使用下标访问map与使用下标访问数组或vector的行为截然不同;用下标访问不存在的元素将导致在map容器中添加一个新的元素,它的键即为该下标的值。

方法一:

复制代码代码如下:

map《string,int》 word_count;
word_count["Peter"]=10;//相当于增加一个键值对
//创建一个map对象,用来记录每个单词出现的次数,十分简洁。
map《string,int》 word_count;
string word;
while(cin>>word)
{
++word_count[word];
}

通常,我们会立即为其赋值,其实就是对同一个对象进行初始化并赋值。而插入元素的另一个方法是:直接使用 insert 成员,其语法更紧凑:

     // if Anna not already in word_count, inserts new element with value 1

     word_count.insert(map::value_type("Anna", 1));



map对象的迭代遍历:

C++ <wbr>STL <wbr>文件读取和容器

结果显示
1
2
3

set类型定义于头文件中。set容器支持大部分map容器的操作,如:构造函数;insert操作;count和find操作; erase操作。两个例外情况是:set不支持下标操作符,而且没有定义mapped_type类型。

map容器是键-值对的集合,而set容器只是单纯的键的集合。当只想知道一个值是否存在时,使用set容器是最合适的。

C++ <wbr>STL <wbr>文件读取和容器

 结果显示

0 1 2 3 4 5 6 7 8 9

但是注意到 ivec.push_back(i); 添加了两次,但是只打印了一次。因为我们只关心key 在不在set中

0 0
原创粉丝点击