C++笔记
# 1.文件相关
# 1.ofstream & ifstream
# 1.简介
ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就是内存空间。 在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O.
stream这个类有两个重要的运算符:
1、插入器(<<)
向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<'\n';就表示把字符串"Write Stdout"和换行字符('\n')输出到标准输出流。
2、析取器(>>)
从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型的数据。
在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。
# 2.打开文件
在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:
void open(const char* filename,int mode,int access);
参数:
filename: 要打开的文件名
mode: 要打开文件的方式
access: 打开文件的属性
打开文件的方式在类ios(是所有流式I/O类的基类)中定义.
mode常用的值如下:
ios::app: 以追加的方式打开文件
ios::ate: 文件打开后定位到文件尾,ios:app就包含有此属性
ios::binary: 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
ios::in: 文件以输入方式打开(文件数据输入到内存)
ios::out: 文件以输出方式打开(内存数据输出到文件)
ios::nocreate: 不建立文件,所以文件不存在时打开失败
ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败
ios::trunc: 如果文件存在,把文件长度设为0
可以用“或”把以上属性连接起来,如ios::out|ios::binary
打开文件的属性取值是:
0:普通文件,打开访问
1:只读文件
2:隐含文件
4:系统文件
可以用“或”或者“+”把以上属性连接起来,如3或1|2就是以只读和隐含属性打开文件。
例如:以二进制输入方式打开文件c:\config.sys
ifstream file1;
file1.open("c:\\config.sys",ios::binary|ios::in,0);
2
# 3. 关闭文件
打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作,就把file1相连的文件关闭。
file1.close();
# 4. 读写文件
读写文件分为文本文件和二进制文件的读取.对于文本文件的读取比较简单,用插入器和析取器就可以了;对于二进制的读取就要复杂些,下要就详细的介绍这两种方式
1、文本文件的读写
文本文件的读写很简单:
用插入器(<<)向文件输出;
用析取器(>>)从文件输入。
假设file1是以输入方式打开,file2以输出打开。
示例如下:
file2<<"I Love You";//向文件写入字符串"I Love You"
int i;
file1>>i;//从文件输入一个整数值。
2
3
4
5
这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些
操纵符 功能 输入/输出
dec 格式化为十进制数值数据 输入和输出
endl 输出一个换行符并刷新此流 输出
ends 输出一个空字符 输出
hex 格式化为十六进制数值数据 输入和输出
oct 格式化为八进制数值数据 输入和输出
setpxecision(int p) 设置浮点数的精度位数 输出
示例:
#include <iostream>
#include <fstream>
using namespace std;
void main( void ) {
//利用ofstream类的构造函数创建一个文件输出流对象来打开文件
ofstream fout( "d:\\mytest.txt" );
if (!fout) {
cout << "文件不能打开" <<endl;
} else {
// 输出到磁盘文件
fout << "Learning C++ is very useful."<< endl;
//关闭文件输出流
fout.close();
//利用ifstream类的构造函数创建一个文件输入流对象
ifstream fin( "d:\\mytest.txt" );
if (!fin) {
cout << "文件不能打开" <<endl;
} else {
char buffer[80];
// 从磁盘文件输入
fin >> buffer;
// 关闭文件输入流
fin.close();
cout << buffer << endl;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 2. open函数
参数问题,
返回值错误列表如下:
点击查看
errno错误码:
errno0 : Success
errno1 : Operation not permitted
errno2 : No such file or directory
errno3 : No such process
errno4 : Interrupted system call
errno5 : Input/output error
errno6 : No such device or address
errno7 : Argument list too long
errno8 : Exec format error
errno9 : Bad file descriptor
errno10 : No child processes
errno11 : Resource temporarily unavailable
errno12 : Cannot allocate memory
errno13 : Permission denied
errno14 : Bad address
errno15 : Block device required
errno16 : Device or resource busy
errno17 : File exists
errno18 : Invalid cross-device link
errno19 : No such device
errno20 : Not a directory
errno21 : Is a directory
errno22 : Invalid argument
errno23 : Too many open files in system
errno24 : Too many open files
errno25 : Inappropriate ioctl for device
errno26 : Text file busy
errno27 : File too large
errno28 : No space left on device
errno29 : Illegal seek
errno30 : Read-only file system
errno31 : Too many links
errno32 : Broken pipe
errno33 : Numerical argument out of domain
errno34 : Numerical result out of range
errno35 : Resource deadlock avoided
errno36 : File name too long
errno37 : No locks available
errno38 : Function not implemented
errno39 : Directory not empty
errno40 : Too many levels of symbolic links
errno41 : Unknown error 41
errno42 : No message of desired type
errno43 : Identifier removed
errno44 : Channel number out of range
errno45 : Level 2 not synchronized
errno46 : Level 3 halted
errno47 : Level 3 reset
errno48 : Link number out of range
errno49 : Protocol driver not attached
errno50 : No CSI structure available
errno51 : Level 2 halted
errno52 : Invalid exchange
errno53 : Invalid request descriptor
errno54 : Exchange full
errno55 : No anode
errno56 : Invalid request code
errno57 : Invalid slot
errno58 : Unknown error 58
errno59 : Bad font file format
errno60 : Device not a stream
errno61 : No data available
errno62 : Timer expired
errno63 : Out of streams resources
errno64 : Machine is not on the network
errno65 : Package not installed
errno66 : Object is remote
errno67 : Link has been severed
errno68 : Advertise error
errno69 : Srmount error
errno70 : Communication error on send
errno71 : Protocol error
errno72 : Multihop attempted
errno73 : RFS specific error
errno74 : Bad message
errno75 : Value too large for defined datatype
errno76 : Name not unique on network
errno77 : File descriptor in bad state
errno78 : Remote address changed
errno79 : Can not access a needed sharedlibrary
errno80 : Accessing a corrupted sharedlibrary
errno81 : .lib section in a.out corrupted
errno82 : Attempting to link in too manyshared libraries
errno83 : Cannot exec a shared librarydirectly
errno84 : Invalid or incomplete multibyte orwide character
errno85 : Interrupted system call should berestarted
errno86 : Streams pipe error
errno87 : Too many users
errno88 : Socket operation on non-socket
errno89 : Destinationaddress required
errno90 : Message too long
errno91 : Protocol wrong type for socket
errno92 : Protocol not available
errno93 : Protocol not supported
errno94 : Socket type not supported
errno95 : Operation not supported
errno96 : Protocol family not supported
errno97 : Address family not supported byprotocol
errno98 : Address already in use
errno99 : Cannot assign requested address
errno100 : Network is down
errno101 : Network is unreachable
errno102 : Network dropped connection onreset
errno103 : Software caused connection abort
errno104 : Connection reset by peer
errno105 : No buffer space available
errno106 : Transport endpoint is alreadyconnected
errno107 : Transport endpoint is notconnected
errno108 : Cannot send after transportendpoint shutdown
errno109 : Too many references: cannot splice
errno110 : Connection timed out
errno111 : Connection refused
errno112 : Host is down
errno113 : No route to host
errno114 : Operation already in progress
errno115 : Operation now in progress
errno116 : Stale NFS file handle
errno117 : Structure needs cleaning
errno118 : Not a XENIX named type file
errno119 : No XENIX semaphores available
errno120 : Is a named type file
errno121 : Remote I/O error
errno122 : Disk quota exceeded
errno123 : No medium found
errno124 : Wrong medium type
errno125 : Operation canceled
errno126 : Required key not available
errno127 : Key has expired
errno128 : Key has been revoked
errno129 : Key was rejected by service
errno130 : Owner died
errno131 : State not recoverable
errno132 : Operation not possible due toRF-kill
errno133 : Unknown error 133
errno134 : Unknown error 134
errno135 : Unknown error 135
errno136 : Unknown error 136
errno137 : Unknown error 137
errno138 : Unknown error 138
errno139 : Unknown error 139
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# 2.数据结构
# 1.bitset
// 构造函数
bitset<4> bitset1; //无参构造,长度为4,默认每一位为0
bitset<8> bitset2(12); //长度为8,二进制保存,前面用0补充
string s = "100101";
bitset<10> bitset3(s); //长度为10,前面用0补充
char s2[] = "10101";
bitset<13> bitset4(s2); //长度为13,前面用0补充
cout << bitset1 << endl; //0000
cout << bitset2 << endl; //00001100
cout << bitset3 << endl; //0000100101
cout << bitset4 << endl; //0000000010101
/*
在进行有参构造时,若参数的二进制表示比bitset的size小,则在前面用0补充(如上面的栗子);
若比bitsize大,参数为整数时取后面部分
参数为字符串时取前面部分(如下面栗子):
*/
bitset<2> bitset1(12); //12的二进制为1100(长度为4),但bitset1的size=2,只取后面部分,即00
string s = "100101";
bitset<4> bitset2(s); //s的size=6,而bitset的size=4,只取前面部分,即1001
char s2[] = "11101";
bitset<4> bitset3(s2); //与bitset2同理,只取前面部分,即1110
cout << bitset1 << endl; //00
cout << bitset2 << endl; //1001
cout << bitset3 << endl; //1110
// 可用的操作符
bitset<4> foo (string("1001"));
bitset<4> bar (string("0011"));
cout << (foo^=bar) << endl; // 1010 (foo对bar按位异或后赋值给foo)
cout << (foo&=bar) << endl; // 0010 (按位与后赋值给foo)
cout << (foo|=bar) << endl; // 0011 (按位或后赋值给foo)
cout << (foo<<=2) << endl; // 1100 (左移2位,低位补0,有自身赋值)
cout << (foo>>=1) << endl; // 0110 (右移1位,高位补0,有自身赋值)
cout << (~bar) << endl; // 1100 (按位取反)
cout << (bar<<1) << endl; // 0110 (左移,不赋值)
cout << (bar>>1) << endl; // 0001 (右移,不赋值)
cout << (foo==bar) << endl; // false (0110==0011为false)
cout << (foo!=bar) << endl; // true (0110!=0011为true)
cout << (foo&bar) << endl; // 0010 (按位与,不赋值)
cout << (foo|bar) << endl; // 0111 (按位或,不赋值)
cout << (foo^bar) << endl; // 0101 (按位异或,不赋值)
// 此外,可以通过 [ ] 访问元素(类似数组),注意最低位下标为0,如下:
bitset<4> foo ("1011");
cout << foo[0] << endl; //1
cout << foo[1] << endl; //1
cout << foo[2] << endl; //0
// 当然,通过这种方式对某一位元素赋值也是可以的,栗子就不放了。
// 可用函数
bitset<8> foo ("10011011");
cout << foo.count() << endl; //5 (count函数用来求bitset中1的位数,foo中共有5个1
cout << foo.size() << endl; //8 (size函数用来求bitset的大小,一共有8位
cout << foo.test(0) << endl; //true (test函数用来查下标处的元素是0还是1,并返回false或true,此处foo[0]为1,返回true
cout << foo.test(2) << endl; //false (同理,foo[2]为0,返回false
cout << foo.any() << endl; //true (any函数检查bitset中是否有1
cout << foo.none() << endl; //false (none函数检查bitset中是否没有1
cout << foo.all() << endl; //false (all函数检查bitset中是全部为1
cout << foo.flip(2) << endl; //10011111 (flip函数传参数时,用于将参数位取反,本行代码将foo下标2处"反转",即0变1,1变0
cout << foo.flip() << endl; //01100000 (flip函数不指定参数时,将bitset每一位全部取反
cout << foo.set() << endl; //11111111 (set函数不指定参数时,将bitset的每一位全部置为1
cout << foo.set(3,0) << endl; //11110111 (set函数指定两位参数时,将第一参数位的元素置为第二参数的值,本行对foo的操作相当于foo[3]=0
cout << foo.set(3) << endl; //11111111 (set函数只有一个参数时,将参数下标处置为1
cout << foo.reset(4) << endl; //11101111 (reset函数传一个参数时将参数下标处置为0
cout << foo.reset() << endl; //00000000 (reset函数不传参数时将bitset的每一位全部置为0
string s = foo.to_string(); //将bitset转换成string类型
unsigned long a = foo.to_ulong(); //将bitset转换成unsigned long类型
unsigned long long b = foo.to_ullong(); //将bitset转换成unsigned long long类型
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 3. 关键字
# 1.explicit
转自这里:https://blog.csdn.net/guoyunfei123/article/details/89003369
首先, C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).
那么显示声明的构造函数和隐式声明的有什么区别呢? 我们来看下面的例子:
class CxString // 没有使用explicit关键字的类声明, 即默认为隐式声明
{
public:
char *_pstr;
int _size;
CxString(int size)
{
_size = size; // string的预设大小
_pstr = malloc(size + 1); // 分配string的内存
memset(_pstr, 0, size + 1);
}
CxString(const char *p)
{
int size = strlen(p);
_pstr = malloc(size + 1); // 分配string的内存
strcpy(_pstr, p); // 复制字符串
_size = strlen(_pstr);
}
// 析构函数这里不讨论, 省略...
};
// 下面是调用:
CxString string1(24); // 这样是OK的, 为CxString预分配24字节的大小的内存
CxString string2 = 10; // 这样是OK的, 为CxString预分配10字节的大小的内存
CxString string3; // 这样是不行的, 因为没有默认构造函数, 错误为: “CxString”: 没有合适的默认构造函数可用
CxString string4("aaaa"); // 这样是OK的
CxString string5 = "bbb"; // 这样也是OK的, 调用的是CxString(const char *p)
CxString string6 = 'c'; // 这样也是OK的, 其实调用的是CxString(int size), 且size等于'c'的ascii码
string1 = 2; // 这样也是OK的, 为CxString预分配2字节的大小的内存
string2 = 3; // 这样也是OK的, 为CxString预分配3字节的大小的内存
string3 = string1; // 这样也是OK的, 至少编译是没问题的, 但是如果析构函数里用free释放_pstr内存指针的时候可能会报错, 完整的代码必须重载运算符"=", 并在其中处理内存释放
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
上面的代码中, "CxString string2 = 10;" 这句为什么是可以的呢? 在C++中, 如果的构造函数只有一个参数时, 那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象. 也就是说 "CxString string2 = 10;" 这段代码, 编译器自动将整型转换为CxString类对象, 实际上等同于下面的操作:
CxString string2(10);
或
CxString temp(10);
CxString string2 = temp;
2
3
4
但是, 上面的代码中的_size代表的是字符串内存分配的大小, 那么调用的第二句 "CxString string2 = 10;" 和第六句 "CxString string6 = 'c';" 就显得不伦不类, 而且容易让人疑惑. 有什么办法阻止这种用法呢? 答案就是使用explicit关键字. 我们把上面的代码修改一下, 如下:
class CxString // 使用关键字explicit的类声明, 显示转换
{
public:
char *_pstr;
int _size;
explicit CxString(int size)
{
_size = size;
// 代码同上, 省略...
}
CxString(const char *p)
{
// 代码同上, 省略...
}
};
// 下面是调用:
CxString string1(24); // 这样是OK的
CxString string2 = 10; // 这样是不行的, 因为explicit关键字取消了隐式转换
CxString string3; // 这样是不行的, 因为没有默认构造函数
CxString string4("aaaa"); // 这样是OK的
CxString string5 = "bbb"; // 这样也是OK的, 调用的是CxString(const char *p)
CxString string6 = 'c'; // 这样是不行的, 其实调用的是CxString(int size), 且size等于'c'的ascii码, 但explicit关键字取消了隐式转换
string1 = 2; // 这样也是不行的, 因为取消了隐式转换
string2 = 3; // 这样也是不行的, 因为取消了隐式转换
string3 = string1; // 这样也是不行的, 因为取消了隐式转换, 除非类实现操作符"="的重载
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
**explicit关键字的作用就是防止类构造函数的隐式自动转换.
上面也已经说过了, explicit关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的, 所以explicit关键字也就无效了.** 例如:
class CxString // explicit关键字在类构造函数参数大于或等于两个时无效
{
public:
char *_pstr;
int _age;
int _size;
explicit CxString(int age, int size)
{
_age = age;
_size = size;
// 代码同上, 省略...
}
CxString(const char *p)
{
// 代码同上, 省略...
}
};
// 这个时候有没有explicit关键字都是一样的
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
但是, 也有一个例外, 就是当除了第一个参数以外的其他参数都有默认值的时候, explicit关键字依然有效, 此时, 当调用构造函数时只传入一个参数, 等效于只有一个参数的类构造函数, 例子如下:
class CxString // 使用关键字explicit声明
{
public:
int _age;
int _size;
explicit CxString(int age, int size = 0)
{
_age = age;
_size = size;
// 代码同上, 省略...
}
CxString(const char *p)
{
// 代码同上, 省略...
}
};
// 下面是调用:
CxString string1(24); // 这样是OK的
CxString string2 = 10; // 这样是不行的, 因为explicit关键字取消了隐式转换
CxString string3; // 这样是不行的, 因为没有默认构造函数
string1 = 2; // 这样也是不行的, 因为取消了隐式转换
string2 = 3; // 这样也是不行的, 因为取消了隐式转换
string3 = string1; // 这样也是不行的, 因为取消了隐式转换, 除非类实现操作符"="的重载
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23