c文件操作

First Post:

Last Update:

Word Count:
3.5k

Read Time:
16 min

文件操作

打开和关闭

头文件

1
2
3
4
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

常用函数

1
2
3
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int close(int fd);

open 函数有两个形式.其中 pathname 是我们要打开的文件名(包含路径名称,缺省是认为在
当前路径下面).flags 可以去下面的一个值或者是几个值的组合.
O_RDONLY:以只读的方式打开文件.
O_WRONLY:以只写的方式打开文件.
O_RDWR:以读写的方式打开文件.
O_APPEND:以追加的方式打开文件.
O_CREAT:创建一个文件.
O_EXEC:如果使用了 O_CREAT 而且文件已经存在,就会发生一个错误.
O_NOBLOCK:以非阻塞的方式打开一个文件.
O_TRUNC:如果文件已经存在,则删除文件的内容.
前面三个标志只能使用任意的一个.如果使用了 O_CREATE 标志,那么我们要使用 open 的第二种形式.还要指定 mode 标志,用来表示文件的访问权限.mode 可以是以下情况的组合.

S_IRUSR 用户可以读 S_IWUSR 用户可以写

S_IXUSR 用户可以执行 S_IRWXU 用户可以读写执行

S_IRGRP 组可以读 S_IWGRP 组可以写

S_IXGRP 组可以执行 S_IRWXG 组可以读写执行

S_IROTH 其他人可以读 S_IWOTH 其他人可以写

S_IXOTH 其他人可以执行 S_IRWXO 其他人可以读写执行

S_ISUID 设置用户执行 ID S_ISGID 设置组的执行 ID

我们也可以用数字来代表各个位的标志.Linux 总共用 5 个数字来表示文件的各种权限.
00000.第一位表示设置用户 ID.第二位表示设置组 ID,第三位表示用户自己的权限位,第四
[18 of 104]
Linux 操作系统 C 语言编程入门
位表示组的权限,最后一位表示其他人的权限.
每个数字可以取 1(执行权限),2(写权限),4(读权限),0(什么也没有)或者是这几个值的和
..
比如我们要创建一个用户读写执行,组没有权限,其他人读执行的文件.设置用户 ID 位那么
我们可以使用的模式是–1(设置用户 ID)0(组没有设置)7(1+2+4)0(没有权限,使用缺省)
5(1+4)即 10705:
open(“temp”,O_CREAT,10705);
如果我们打开文件成功,open 会返回一个文件描述符.我们以后对文件的所有操作就可以
对这个文件描述符进行操作了.

拷贝文件例子

1
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
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>


#define BUFFER_SIZE 4096
int main(int argc, char **argv) {
int from_fd, to_fd;
int bytes_read, bytes_write;
char buffer[BUFFER_SIZE];
char *ptr;
if(argc != 3) {
fprintf(stderr, "Usage: %s fromfile tofile\n\a", argv[0]);
exit(1);
}
//打开源文件
if(-1 == (from_fd = open(argv[1], O_RDONLY))) {
fprintf(stderr, "Open %s Error: %s\n", argv[1], strerror(errno));
exit(1);
}
//创建目的文件
if(-1 == (to_fd = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR))) {
fprintf(stderr, "Open %s Error: %s\n", argv[2], strerror(errno));
exit(1);
}
//拷贝文件
while(bytes_read = read(from_fd, buffer, BUFFER_SIZE)) {
if((bytes_read == -1) && (errno != EINTR)) break;
else if(bytes_read > 0) {
ptr = buffer;
while(bytes_write = write(to_fd, ptr, bytes_read)) {
if((bytes_write == -1) && (errno != EINTR)) break;
else if(bytes_write == bytes_read) break;
else if(bytes_write > 0) {
ptr += bytes_write;
bytes_read -= bytes_write;
}
}
}
}
close(from_fd);
close(to_fd);

return 0;
}

获取文件信息

文件具有各种各样的属性,除了我们上面所知道的文件权限以外,文件还有创建时间,大小等等属性.有时侯我们要判断文件是否可以进行某种操作(读,写等等).这个时候我们可以使用 access 函数.

1
2
#include <unistd.h>;
int access(const char *pathname,int mode);

pathname:是文件名称,mode 是我们要判断的属性.可以取以下值或者是他们的组合.
R_OK 文件可以读,W_OK 文件可以写,X_OK 文件可以执行,F_OK 文件存在.当我们测试成功

,函数返回 0,否则如果有一个条件不符时,返回-1.
如果我们要获得文件的其他属性,我们可以使用函数 stat 或者 fstat.

头文件

1
2
#include <sys/stat.h>;
#include <unistd.h>;

常用函数

1
2
int stat(const char *file_name,struct stat *buf);
int fstat(int filedes,struct stat *buf);

信息结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct stat {
dev_t st_dev; // 设备
ino_t st_ino; // 节点
mode_t st_mode; // 模式
nlink_t st_nlink; // 硬连接
uid_t st_uid; // 用户 ID
gid_t st_gid; // 组 ID
dev_t st_rdev; //设备类型
off_t st_off; // 文件字节数
unsigned long st_blksize; // 块大小
unsigned long st_blocks; // 块数
time_t st_atime; // 最后一次访问时间
time_t st_mtime; // 最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};

stat 用来判断没有打开的文件,而 fstat 用来判断打开的文件.我们使用最多的属性是 st_
mode.通过着属性我们可以判断给定的文件是一个普通文件还是一个目录,连接等等.可以
使用下面几个宏来判断.
S_ISLNK(st_mode):是否是一个连接.S_ISREG 是否是一个常规文件.S_ISDIR 是否是一个目
录 S_ISCHR 是否是一个字符设备.S_ISBLK 是否是一个块设备 S_ISFIFO 是否 是一个 FIFO

件.S_ISSOCK 是否是一个 SOCKET 文件

Misc

1
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
void sys_01() //复制文件函数
{
//打开已经存在的文件
int fd = open("Logan.txt",O_RDONLY);
if(fd == -1)
{
perror("open");
exit(1);
}
//创建文件
int fd2 = open("Newfile",O_CREAT | O_WRONLY | O_EXCL, 0755);
if(fd2 == -1)
{
perror("open2");
exit(1);
}
char buf[2048] = {0};
int count = read(fd,buf,sizeof(buf));
if(count == -1)
{
perror("read");
exit(1);
}
while(count)
{
//将读出的文件写到另一个文件中
int sizeofwrite = write(fd2,buf,count);
printf("write bytes %d\n",sizeofwrite);
count = read(fd,buf,sizeof(buf));
}

close(fd);
close(fd2);
}
void sys_02()//使用lseek 实现空洞文件
{
int fd = open("Logan.txt",O_RDWR);
if(fd == -1)
{
perror("open file");
exit(1);
}
int sizeoffile = lseek(fd,0,SEEK_END);
printf("Size fo file %d\n",sizeoffile);
lseek(fd,1000,SEEK_END);
printf("Size fo file %d\n",sizeoffile);
write(fd,"A",1);
close(fd);
}
//stat
//lstat
//access
//chmod
//chown
//truncate
//link
//symlink
//readlink
//unlink
//rename
//fcntl
//dup,dup2
//dentry,inode
//解决gcc编译过程中的c99语法报错问题
//alias gcc = 'gcc -std=gnu99'

void sys_03() //stat
{
//穿透函数---(在获取软链接文件大小等内容时,链接到目标文件获取大小)
//int stat(const char *path,struct stat *buf)
//int fstat(int fd, struct stat *buf);
//不穿透函数---(在获取软链接文件大小等内容时,直接就是本文件大小)
//int lstat(const char *path, struct stat *buf)
/*
* struct stat {
* dev_t st_dev; //文件设备编号
* ino_t st_ino; //节点
* mode_t st_mode; //文件的类型和存储权限
* nlink_t st_nlink; //链接到该文件的数目
* uid_t st_uid; //用户ID
* gid_t st_gid; //组ID
* dev_t st_rdev; //(设备类型) 若此文件为设备文件,则其为设备编号
* off_t st_size; //文件字节数(文件大小)
* blksize_t st_blksize;//块大小(文件系统的I/O缓冲区大小
* blkcnt_t st_blocks; //块数
* time_t st_atime; //最后一次访问时间
* time_t st_mtime; //最后一次修改时间
* time_t st_ctime //最后一次改变时间
* };
* */
struct stat st;
int fd = -1;
fd = open("Logan.txt",O_RDWR);
if(fd == -1)
{
perror("open file");
exit(1);
}
fstat(fd,&st);
//获取文件大小
printf("Size of file %d\n",st.st_size);
}
void sys_04() //access()
{

//int access(const char *pathname,int mode);
//测试指定文件是否拥有某种权限

int ret = access("Logan.txt",W_OK);
if(ret == -1)
{
perror("access");
exit(1);
}
printf("you can write this file.\n");
}
//实现一个chmod 命令
void sys_05(int argc,char **argv) //chmod()
{
//strtol(const char *nptr,char **endptr, int base); //**endptr 通常为NULL
int ret = chmod(argv[2],strtol(argv[1],NULL,8)); //八进制
if(argc < 3)
{
printf("Useage: ./a.out 755 filename");
}
if(ret == -1)
{
perror("Chmod");
exit(1);
}
printf("Mode changed!\n");
//strtol
}
void sys_06(int argc,char **argv) //chown()
{
/*
* int chown(const char *pathname, uid_t owner,gid_t group);
* int fchown(int fd,uid_t owner,gid_t group);
* int lchown(const char *pathname,uid_t owner,gid_t group);
* uid_t && gid_t 可通过 etc/passwd 获取
* 查看配置文件 man 5 passwd
* */
}
void sys_07() //truncate()
{
/*
* int truncate(const char *path, off_t length);
* int ftruncate(int fd, off_t length);
* */
int trun = truncate("Logan.txt",12);
if(trun == -1)
{
perror("truncate");
exit(1);
}
printf("OK!");

}
void sys_08()
{
/*创建一个硬链接
*int link(const char *oldpath,const char *newpath);
*创建软链接
*syslink()
*读软链接对应的文件名,不是内容
*readlink()

*
*删除一个文件的目录项并减少它的链件数,若成功则返回0,否则返回1
*如果想通过这个函数来成功删除文件,你就必须拥有这个目录的可执行和写权限
*unlink()
* */
}
void sys_09()//rename() 更改名字函数
{
//rename()
//renameat(int olddirfd,const char *oldpath,int newdirfd,const char *newpath);

//renameat2(int olddirfd ,const char *oldpath, int newdirfd, const char *newpath, unsigned int flags);
int ret = rename("./Logan.txt","OK.txt");
if (ret == -1)
{
perror("rename");
exit(1);
}
printf("OK!");
}
void sys_10(int argc,char **argv) //目录操作函数
{
//chdir() 改变目录 -- int chdir(const char *path);
//getcwd() 获取路径
//mkdir() 创建目录
//rmdir() 删除空目录
//opendir 打开一个目录 打开成功非0 失败返回 NULL
//
//readdir 读目录
//struct dirent *readdir(DIR *dirp);
//
//返回值:
//struct dirent
//{
// ino_t dino; //此目录进入点的inode
// ff_t d_off; //目录文件开头至此目录进入点的位移
// signed short int d_reclen; //d_name的长度,不包含NULL字符
// unsigned char d_type; //d_name所指的文件类型
// har d_name[256]; //文件名
//}
//d_type
//DT_BLK -块设备
//DT_CHR -字符设备
//DT_DIR -目录
//DT_LNK -软链接
//DT_FIFO -管道
//DT_REG -普通文件
//DT_SOCK - 套接字
//DT_UNKNOWN -未知
//
//-D_BSD_SOURCE 编译时添加的宏定义
//
//
//closedir 关闭目录
//
char path[256] = {0};
if(argc < 2)
{
printf("Useage: ./a.out path");
exit(1);
}
int ret = chdir(argv[1]);
if(ret == -1)
{
perror("chdir");
exit(1);
}
getcwd(path, sizeof(path));
printf("current path: %s\n",path);
}
//使用readdir函数来获取当前普通文件个数实例
int getFileNum(char *root)
{
DIR* dir = NULL;
dir = opendir(root);
if(dir == NULL)
{
perror("opendir");
exit(1);
}
//便利目录
struct dirent* ptr = NULL;
char path_name[1024] = {0};
int total = 0;
while( (ptr = readdir(dir)) )
{
//过滤. 和 ..
if(strcmp(ptr->d_name,".") == 0 || strcmp(ptr->d_name,"..") == 0)
{
continue;
}
//如果是目录
if(ptr->d_type == DT_DIR)
{
//递归 读目录
sprintf(path_name,"%s/%s",root,ptr->d_name);
total += getFileNum(path_name);
}
//如果是普通文件
if(ptr->d_type == DT_REG)
{
total++;
}
}
closedir(dir);
return total;

}
void sys_11(int argc,char **argv)
{
if(argc < 2)
{
printf("Usage:./a.out filepath\n");
exit(1);
}
printf("Number of Curent file:%d\n",getFileNum(argv[1]));
}
void sys_12()
{
/*文件描述符的复制
*
*dup(),dup2()
*dup()返回一个最小的未被占用的文件描述符号
*dup2() dup2(int odfd,int newfd);
*打开文件描述符前,若newfd已经存在先关闭newfd,然后再拷贝
*若oldfd与newfd是同一个文件描述符,则不会关闭newfd
*
* */
int fd = open("Logan.txt",O_RDWR);
if(fd == -1)
{
perror("open");
exit(1);
}
printf("file open fd = %d\n",fd);
//找到进程文件描述表中 第一个 可用的文件描述符
//将参数指定的文件复制到该描述符后,返回这个描述符
int ret = dup(fd);
if(ret == -1)
{
perror("dup");
exit(1);
}
printf("dup fd = %d\n",ret);
char* buf = "你是猴子派来的救兵吗?\n";
char *buf_2 = "你大爷的! 我是程序猿!!\n";
write(fd, buf,strlen(buf));
write(ret, buf_2,strlen(buf_2));
close(fd);
close(ret);
}
void sys_13()
{
//fcntl
//
//int fcntl(int fd, int cmd)
//int fcntl(int fd, int cmd, long arg) (目前使用)
//int fcntl(int fd, int cmd, struct flock *lock)
//
//复制现有的文件描述符 F_DUPFD
//获得/设置文件描述符标记 --cmd F_GETFD / F_SETFD
//
//获得/设置文件状态标记 --CMD
// ***F——GETFL
//只读打开: O_RDONLY
//只写打开: O_WRONLY
//读写打开: O_RDWR
//执行打开: O_EXEC
//搜索打开目录: O_SEARCH
//追加写: A_PPEND
//非阻塞模式: O_NONBLOCK
// ***F_SETFL
//O_APPEND
//O_NONBLOCK
//
//获得/设置异步I/O所有权 --cmd F_GETOWN / F_SETOWN
//获得/设置记录锁 --cmd F_GETLK / F_SETLK / F_SETLKW

char *txt1 = "我是"
char *txt2 = "Logan"
int fd = open("Logan.txt",O_WRONLY);
if(fd == -1)
{
perror("open");
exit(1);
}
if(write(fd,txt1,strlen(txt1)) == -1)
{
perror("write");
eixt(1);
}
int flag = fcntl(fd, F_GETFL, 0);


}

int main(int argc,char **argv)
{
//sys_01();
//sys_02();
//sys_03();
//sys_04();
//sys_05(argc,argv);
//sys_07();
//sys_09();
//sys_10(argc,argv);
//sys_11(argc,argv);
sys_12();
return 0;
}

实现 ls

1
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
#include <sys/types.h> 
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("Useage: ./a.out filename\n");
exit(1);
}
struct stat st;
int ret = stat(argv[1],&st);
if(ret == -1)
{
perror("stat");
exit(1);
}
//储存文件类型和访问权限
char perms[11] = {0};
//判断文件类型
switch(st.st_mode & S_IFMT)
{
case S_IFLNK:
perms[0] = '1';
break;
case S_IFDIR:
perms[0] = 'd';
break;
case S_IFREG:
perms[0] = '-';
break;
case S_IFBLK:
perms[0] = 'b';
break;
case S_IFCHR:
perms[0] = 'c';
break;
case S_IFSOCK:
perms[0] = 's';
break;
case S_IFIFO:
perms[0] = 'P';
break;
default:
perms[0] = '?';
break;

}
//判断文件的访问权限
//文件所有者
perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';
//文件所属组
perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
//其他人
perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';

//硬件链接计数
int linkNum = st.st_nlink;
//文件所有者
char* fileUser = getpwuid(st.st_uid)->pw_name;
//文件所属组
char* fileGrp = getgrgid(st.st_gid)->gr_name;
//文件大小
int fileSize = (int)st.st_size;
//修改时间
char* time = ctime(&st.st_mtime);
char mtime[512] = {0};
strncpy(mtime, time, strlen(time)-1);

char buf[1024];
sprintf(buf,"%s %d %s %s %d %s %s",perms,linkNum,fileUser,fileGrp,fileSize,mtime,argv[1]);
printf("%s\n",buf);

return 0;
}
打赏点小钱
支付宝 | Alipay
微信 | WeChat