CPP 零散知识集合
格式要求
- 整理reference, 而不是粘贴链接或者全部复制内容
- 严格层次
C++文件读写详解
这里主要是讨论fstream的内容:
#include <fstream>
ofstream //文件写操作 内存写入存储设备,默认为ios::out
ifstream //文件读操作,存储设备读区到内存中,默认为ios::in
fstream //读写操作,对打开的文件可进行读写操作,默认为ios::in|ios::out
1.打开文件
函数:open()
public member function
void open ( const char * filename,
ios_base::openmode mode = ios_base::in | ios_base::out );
void open(const wchar_t *_Filename,
ios_base::openmode mode= ios_base::in | ios_base::out,
int prot = ios_base::_Openprot);
参数: filename 操作文件名
mode 打开文件的方式
prot 打开文件的属性 //基本很少用到
打开文件的方式在ios类(所以流式I/O的基类)中定义,有如下几种方式:
ios::in | 为输入(读)而打开文件 |
---|---|
ios::out | 为输出(写)而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 所有输出附加在文件末尾 |
ios::trunc | 如果文件已存在则先删除该文件 |
ios::binary | 二进制方式 |
这些方式是能够进行组合使用的,以“或”运算(“|”)的方式:例如
ofstream out;
out.open("Hello.txt", ios::out|ios::binary)
打开文件的属性同样在ios类中也有定义:
0 | 普通文件,打开操作 |
---|---|
1 | 只读文件 |
2 | 隐含文件 |
4 | 系统文件 |
对于文件的属性也可以使用“或”运算和“+”进行组合使用,这里就不做说明了。
直接调用了其默认的打开方式
ofstream out("test.txt", ios::out);
ifstream in("test.txt", ios::in);
fstream foi("test.txt", ios::in|ios::out);
当使用默认方式进行对文件的操作时,你可以使用成员函数is_open()对文件是否打开进行验证
2.关闭文件
f.close()
3.文本文件的读写
写
// writing on a text file
#include <fstream>
int main () {
ofstream out("out.txt");
if (out.is_open())
{
out << "This is a line.\n";
out << "This is another line.\n";
out.close();
}
return 0
}
//结果: 在out.txt中写入:
This is a line.
This is another line
读
// reading a text file
#include <iostream>
#include <fstream>
#include <cstdlib>
int main () {
char buffer[256];
ifstream in("test.txt");
if (! in.is_open())
{ cout << "Error opening file"; exit (1); }
while (!in.eof() )
{
in.getline (buffer,100);
cout << buffer << endl;
}
return 0;
}
//结果 在屏幕上输出
This is a line.
This is another line
状态标志符的验证
bad()
如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。
fail()
除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true ,例如当想要读入一个整数,而获得了一个字母的时候。
eof()
如果读文件到达文件末尾,返回true。
good()
这是最通用的:如果调用以上任何一个函数返回true 的话,此函数返回 false 。
要想重置以上成员函数所检查的状态标志,你可以使用成员函数clear(),没有参数。
读和写流指针
get and put stream pointers
所有输入/输出流对象(i/o streams objects)都有至少一个流指针:
- ifstream, 类似istream, 有一个被称为get pointer的指针,指向下一个将被读取的元素。
- ofstream, 类似 ostream, 有一个指针 put pointer ,指向写入下一个元素的位置。
- fstream, 类似 iostream, 同时继承了get 和 put
通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针:
tellg() 和 tellp()
这两个成员函数不用传入参数,返回pos_type 类型的值(根据ANSI-C++ 标准) ,就是一个整数,代表当前get 流指针的位置 (用tellg) 或 put 流指针的位置(用tellp).
seekg() 和seekp()
这对函数分别用来改变流指针get 和put的位置。两个函数都被重载为两种不同的原型:
seekg ( pos_type position ); seekp ( pos_type position );
使用这个原型,流指针被改变为指向从文件开始计算的一个绝对位置。要求传入的参数类型与函数 tellg 和tellp 的返回值类型相同。
seekg ( off_type offset, seekdir direction ); seekp ( off_type offset, seekdir direction );
使用这个原型可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset)。它可以是:
ios::beg 从流开始位置计算的位移 ios::cur 从流指针当前位置开始计算的位移 ios::end 从流末尾处开始计算的位移
流指针 get 和 put 的值对文本文件(text file)和二进制文件(binary file)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。由于这个原因,建议对以文本文件模式打开的文件总是使用seekg 和 seekp的第一种原型,而且不要对tellg 或 tellp 的返回值进行修改。对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。
以下例子使用这些函数来获得一个二进制文件的大小:
// obtaining file size
#include <iostream>
#include <fstream>
const char * filename = "test.txt";
int main () {
long l,m;
ifstream in(filename, ios::in|ios::binary);
l = in.tellg();
in.seekg (0, ios::end)
m = in.tellg();
in.close();
cout << "size of " << filename;
cout << " is " << (m-l) << " bytes.\n"
return 0;
}
//结果:
size of example.txt is 40 bytes.
4.二进制文件
在二进制文件中,使用<< 和>>,以及函数(如getline)来操作符输入和输出数据,没有什么实际意义,虽然它们是符合语法的。
文件流包括两个为顺序读写数据特殊设计的成员函数:write 和 read。第一个函数 (write) 是ostream 的一个成员函数,都是被ofstream所继承。而read 是istream 的一个成员函数,被ifstream 所继承。类 fstream 的对象同时拥有这两个函数。它们的原型是:
write ( char * buffer, streamsize size );
read ( char * buffer, streamsize size );
这里 buffer 是一块内存的地址,用来存储或读出数据。参数size 是一个整数值,表示要从缓存(buffer)中读出或写入的字符数。
// reading binary file
#include <iostream>
#include <fstream>
const char * filename = "test.txt";
int main () {
char * buffer;
long size;
ifstream in (filename, ios::in|ios::binary|ios::ate);
size = in.tellg();
in.seekg (0, ios::beg);
buffer = new char [size];
in.read (buffer, size);
in.close();
cout << "the complete file is in a buffer";
delete[] buffer;
return 0;
}
//运行结果:
The complete file is in a buffer
5.缓存和同步(Buffers and Synchronization)
当我们对文件流进行操作的时候,它们与一个streambuf 类型的缓存(buffer)联系在一起。这个缓存(buffer)实际是一块内存空间,作为流(stream)和物理文件的媒介。例如,对于一个输出流, 每次成员函数put (写一个单个字符)被调用,这个字符不是直接被写入该输出流所对应的物理文件中的,而是首先被插入到该流的缓存(buffer)中。
当缓存被排放出来(flush)时,它里面的所有数据或者被写入物理媒质中(如果是一个输出流的话),或者简单的被抹掉(如果是一个输入流的话)。这个过程称为同步(synchronization),它会在以下任一情况下发生:
- 当文件被关闭时: 在文件被关闭之前,所有还没有被完全写出或读取的缓存都将被同步。
- 当缓存buffer 满时:缓存Buffers 有一定的空间限制。当缓存满时,它会被自动同步。
- 控制符明确指明:当遇到流中某些特定的控制符时,同步会发生。这些控制符包括:flush 和endl。
- 明确调用函数sync(): 调用成员函数sync() (无参数)可以引发立即同步。这个函数返回一个int 值,等于-1 表示流没有联系的缓存或操作失败。
C++ 一次读取文件全部内容
1. 读取一个string
std::ifstream in("some.file");
std::string some_str;
in >> some_str;
这种方法的问题在于,遇到回车空格等分隔符的时候,就不会再读取了。
2. 读取文件全部内容
iostream著名专家Dietmar Kuehl给出了两个读取方法
#include<ifstream>
#include<iterator>
#include<string>
std::ifstream in("some.file");
std::isreambuf_iterator<char> begin(in);
std::isreambuf_iterator<char> end;
std::string some_str(begin, end);
或
#include<fstream>
#include<sstream>
#include<string>
std::ifstream in("some.file");
std::ostringstream tmp;
tmp << in.rdbuf();
std::string str = tmp.str();
C++ 数字, string转换
to_string函数,这是C++11新增的.等等函数(这几个就是string转int,long,以及long long啦~)
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)
to number
int stoi(string s)
long stol(string s)
long long stoll(string s)
C++ 字符串分割
"This is a statement"
使用 C++ 将这个字符串分割成四个子字符串: "This", "is", "a", "statement"?
超级简略版
#include <iostream>
using std::cout;
#include <iterator>
using std::istream_iterator; using std::ostream_iterator;
#include <string>
using std::string;
#include <sstream>
using std::istringstream;
#include <algorithm>
using std::copy;
int main()
{
string str("This is a statement");
istringstream iss(str), end;
copy(istream_iterator<string>(iss), istream_iterator<string>(end), ostream_iterator<string>(cout, "\n"));
return 0;
}
这个技巧不足为道,C++ Primer 5th 中 10.4.2. iostream Iterators 里有介绍。
工程实用版
上面的方法虽然简洁,但是仅能够按空格分隔,在实际工程中,显然是没太大用处的,抽出一个 split
函数还是很有必要的。
#include <iostream>
using std::cout; using std::getline;
#include <iterator>
using std::ostream_iterator;
#include <string>
using std::string;
#include <sstream>
using std::istringstream;
#include <vector>
using std::vector;
#include <algorithm>
using std::copy;
vector<string> &split(const string &str, char delim, vector<string> &elems, bool skip_empty = true) {
istringstream iss(str);
for (string item; getline(iss, item, delim); )
if (skip_empty && item.empty()) continue;
else elems.push_back(item);
return elems;
}
int main()
{
string str("Windows,Linux,,MacOS,");
vector<string> result;
split(str, ',', result);
copy(result.begin(), result.end(), ostream_iterator<string>(cout, "\n"));
return 0;
}
这样我们不仅可以用别的字符来做分隔,还可以选择跳过空白字符串,如我故意在 Linux 和 MacOS 之间放了两个相邻的 ,,
,若不做处理和判断的话,就会多分割出一个空白字符来。
继续改进版
上面这样,还有一个限制未解决,即如果我想按多种字符来分隔呢?如按照 ,
,|
以及空格,来分割字符串。
可以利用 find_first_of
来实现。
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <iostream>
vector<string> &split( const string &str, const string &delimiters, vector<string> &elems, bool skip_empty = true ) {
string::size_type pos, prev = 0;
while ( ( pos = str.find_first_of(delimiters, prev) ) != string::npos ) {
if ( pos > prev ) {
if ( skip_empty && 1 == pos - prev ) break;
elems.emplace_back( str, prev, pos - prev );
}
prev = pos + 1;
}
if ( prev < str.size() ) elems.emplace_back( str, prev, str.size() - prev );
return elems;
}
int main()
{
string str( "Windows,Linux||MacOS All" );
vector<string> result;
for ( const auto &s : split( str, ",| ", result ) )
std::cout << s << " ";
std::cout << std::endl;
}
这个版本基本满足大部分工程上的需要了。
C++ std::pair的用法
1. pair的应用
pair是将2个数据组合成一个数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。
2. make_pair函数
template pair make_pair(T1 a, T2 b) { return pair(a, b); }
很明显,我们可以使用pair的构造函数也可以使用make_pair来生成我们需要的pair。 一般make_pair都使用在需要pair做参数的位置,可以直接调用make_pair生成pair对象很方便,代码也很清晰。 另一个使用的方面就是pair可以接受隐式的类型转换,这样可以获得更高的灵活度。灵活度也带来了一些问题如:
std::pair<int, float>(1, 1.1);
std::make_pair(1, 1.1);
是不同的,第一个就是float,而第2个会自己匹配成double
3. pair使用
类模板:template <class T1, class T2> struct pair
参数:T1是第一个值的数据类型,T2是第二个值的数据类型。
功能:pair将一对值组合成一个值,这一对值可以具有不同的数据类型(T1和T2),两个值可以分别用pair的两个公有函数first和second访问。
具体用法:
3.1 定义(构造):
pair<int, double> p1; //使用默认构造函数
pair<int, double> p2(1, 2.4); //用给定值初始化
pair<int, double> p3(p2); //拷贝构造函数
3.2 访问两个元素
(通过first和second):
pair<int, double> p1; //使用默认构造函数
p1.first = 1;
p1.second = 2.5;
cout << p1.first << ' ' << p1.second << endl;
输出结果:1 2.5
3.3 赋值operator =
(1)利用make_pair:
pair<int, double> p1;
p1 = make_pair(1, 1.2);
(2)变量间赋值:
pair<int, double> p1(1, 1.2);
pair<int, double> p2 = p1;
4. typedef简化
由于pair类型的使用比较繁琐,因为如果要定义多个形同的pair类型的时候,可以时候typedef简化声明:
typedef pair<string, string> author;
author pro("May", "Lily");
author joye("James", "Joyce");
C++ sort函数用于vector向量的排序
一般类型
(int,double,char)
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> vec;
for (int i = 0; i < 10;i++)
vec.push_back(rand());
sort(vec.begin(), vec.end());
for (vector<int>::iterator it = vec.begin(); it < vec.end(); it++)
cout << *it << " ";
结构体或对象类型
结构体定义如下
typedef struct ClassDis
{
double distance;
int class1;
int class2;
}ClassDis;
需要自己定义一个比较函数(
//自定义“小于”
bool comp(const ClassDis &a, const ClassDis &b)
{
return a.distance < b.distance;
}
vector<ClassDis> vec;
sort(vec.begin(), vec.end(), comp);
C++中set的用法
set的特性是,所有元素都会根据元素的键值自动排序,set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值。set不允许两个元素有相同的键值。
set的各成员函数列表如下:
- begin()--返回指向第一个元素的迭代器
- clear()--清除所有元素
- count()--返回某个值元素的个数
- empty()--如果集合为空,返回true
- end()--返回指向最后一个元素的迭代器
- equal_range()--返回集合中与给定值相等的上下限的两个迭代器
- erase()--删除集合中的元素
- find()--返回一个指向被查找到元素的迭代器
- get_allocator()--返回集合的分配器
- insert()--在集合中插入元素
- lower_bound()--返回指向大于(或等于)某值的第一个元素的迭代器
- key_comp()--返回一个用于元素间值比较的函数
- max_size()--返回集合能容纳的元素的最大限值
- rbegin()--返回指向集合中最后一个元素的反向迭代器
- rend()--返回指向集合中第一个元素的反向迭代器
- size()--集合中元素的数目
- swap()--交换两个集合变量
- upper_bound()--返回大于某个值元素的迭代器
- value_comp()--返回一个用于比较元素间的值的函数
#include<set>
#include<iostream>
using namespace std;
int main()
{
int i;
int arr[5] = {0,1,2,3,4};
set<int> iset(arr,arr+5);
iset.insert(5);
cout<<"size:"<<iset.size()<<endl;
cout<<"3 count = "<<iset.count(3)<<endl;
iset.erase(1);
set<int>::iterator ite1 = iset.begin();
set<int>::iterator ite2 = iset.end();
for(;ite1!=ite2;ite1++)
{
cout<<*ite1;
}
cout<<endl;
ite1 = iset.find(3);
if(ite1!=iset.end())
cout<<"3 found"<<endl;
ite1 = iset.find(1);
if(ite1!=iset.end())
cout<<"1 not found"<<endl;
}
C++中的STL中map用法详解
自动建立Key - value的对应。key 和 value可以是任意你需要的类型。
1. 使用map构造函数
include <map>
std:map<int,string> personnel;
这样就定义了一个用int作为索引,并拥有相关联的指向string的指针.
2. 数据的插入
在构造map容器后,就可以往里面插入数据了
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
map<int, string> mapStudent;
//第一种:用insert函数插入pair数据
mapStudent.insert(pair<int, string>(1, "student_one"));
// 第二种:用insert函数插入value_type数据
mapStudent.insert(map<int, string>::value_type (2, "student_two"));
//第三种:用数组方式插入数据,下面举例说明
mapStudent[3] = "student_three";
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<' '<<iter->second<<endl;
}
用insert函数插入数据,当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式可以覆盖以前该关键字对应的值
mapStudent.insert(map<int, string>::value_type (1, "student_one"));
mapStudent.insert(map<int, string>::value_type (1, "student_two"));
上面这两条语句执行后,map中1这个关键字对应的值是“student_one”,第二条语句并没有生效.
pair<map<int, string>::iterator, bool> Insert_Pair;
Insert_Pair = mapStudent.insert(map<int, string>::value_type (1, "student_one"));
我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个map的迭代器,如果插入成功的话Insert_Pair.second应该是true的,否则为false。
3. map的大小
int nSize = mapStudent.size();
4. 数据的遍历
这里也提供三种方法,对map进行遍历
4.1 前向迭代器
上面举例程序中有
4.2 反相迭代器
map<int, string>::reverse_iterator iter;
for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++) cout<<iter->first<<" "<<iter->second<<endl;
4.3 数组
for(int nindex = 1; nindex <= nSize; nindex++)
cout<<mapStudent[nindex]<<endl;
5. 查找并获取map中的元素
5.1 用count函数
其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了
5.2 用find函数
它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器。
查找map中是否包含某个关键字条目用find()方法,传入的参数是要查找的key,在这里需要提到的是begin()和end()两个成员,
分别代表map对象中第一个条目和最后一个条目,这两个数据的类型是iterator.
map<int, string>::iterator iter;
iter = mapStudent.find(1);
if(iter != mapStudent.end())
cout<<"Find, the value is "<<iter->second<<endl;
else
cout<<"Do not Find"<<endl;
通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据 iterator->first和 iterator->second分别代表关键字和存储的数据。
6. 从map中删除元素
iterator erase(iterator it);//通过一个条目对象删除
iterator erase(iterator first,iterator last)//删除一个范围
size_type erase(const Key&key);//通过关键字删除
clear(); //就相当于enumMap.erase(enumMap.begin(),enumMap.end());
map<int, string>::iterator iter;
iter = mapStudent.find(1);
mapStudent.erase(iter);
int n = mapStudent.erase(1);//如果删除了会返回1,否则返回0
mapStudent.erase( mapStudent.begin(), mapStudent.end() );
7. map的基本操作函数
begin() 返回指向map头部的迭代器
clear() 删除所有元素
count() 返回指定元素出现的次数
empty() 如果map为空则返回true
end() 返回指向map末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
size() 返回map中元素的个数
swap() 交换两个map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数
带参数的的main函数
#include<iostream>
using namespace std;
int main(int argc, char* argv[])
{
int i;
cout << "当前文件的路径:" << argv[0] << endl;
for (i = 1; i < argc; i++)
{
cout << i << "," << argv[i] << endl;
}
return 0;
}