Linux文件IO操作

网友投稿 708 2022-05-29

文件操作是linux系统中最常见的操作之一,关于文件的输入输出操作,分为基于文件描述符的I/O操作和基于流的I/O操作。本篇介绍基于文件描述符的I/O操作。

文件类型

首先介绍下Linux系统中文件的各种类型与符号表示。

基于文件描述符的I/O操作

文件描述符

Linux操作系统内核利用文件描述符来访问文件,文件描述符是一个非负整数,用于描述被打开文件的索引值,它指向该文件的相关信息记录表。

文件描述符的有效范围是0到1023,其中0、1、2分别用于标准输入、标准输出和标准出错。

文件重定向

重定向标准输出

不使用系统标准输出的默认设备,而是将输出结果直接写在一个新的文件中,命令格式如下:

command 1> filename # 把标准输出重定向到filename文件中 command > filename # 把标准输出重定向到filename文件中 command >> filename # 把标准输出重定向到filename文件中,方式是追加在现有内容的后面 > myfile # 创建一个长度为0的空文件

command代表用户所熟悉的shell命令

重定向标准输入

不使用系统标准输入的默认设备,而是引用其它文件的内容或是其它命令的输出,命令格式如下:

command < filename # 以filename文件的内容作为command命令的标准输入 command < file1 > file2 # 以file1文件的内容作为command命令的标准输入,并以file2文件的内容作为command命令的标准输出 command << delimiter # 从标准输入中读入,直到遇到delimiter分界符

重定向标准出错

将系统指向的错误信息重定向到一个文件中,而不使用默认的显示器等输出设备,命令格式如下:

command 2> filename # 把标准出错信息重定向到filename文件中 command 2>> filename # 把标准出错信息重定向到filename文件中(追加)

文件的创建、打开、关闭

open()函数

调用该函数可以打开或创建一个文件,函数原型为:

#include #include #include int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);

运行成功返回文件描述符,运行出错返回-1。

参数flag用于描述文件的打开方式。

参数mode用于指定所创建文件的权限。

使用open函数打开或创建一个文件,open_file.c:

#include #include #include #include #include #include #define FLAGS O_WRONLY|O_CREAT|O_TRUNC #define MODE S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH int main(void) { const char *pathname; int fd; char pn[30]; printf("Input the pathname[<30 strings]:"); gets(pn); pathname = pn; if((fd = open(pathname, FLAGS, MODE)) == -1) { printf("error, open file failed!\n"); exit(1); } printf("OK, open file successful!\n"); printf("fd=%d\n", fd); return 0; }

编译后执行:

$ ./open_file Input the pathname[<30 strings]:./hello.txt OK, open file successful! fd=3

程序在当前目录创建了hello.txt文件,进一步查看该文件的信息:

ls -l hello.txt -rwxr-xr-x 1 deeplearning deeplearning 0 12月 20 19:59 hello.txt

可以看到文件的权限为rwxr-xr-x,与程序中的MODE宏定义一致。

create()函数

用于创建文件,函数原型为:

#include #include #include int creat(const char *pathname, mode_t mode);

运行成功返回以只写方式打开的文件描述符,运行出错返回-1。

close()函数

用于关闭文件,函数原型为:

#include int close(int fd);

运行成功返回0,运行出错返回-1。

文件的定位

每个已打开的文件都有一个与其相关联的“当前文件位移量”,它是一个非负整数,用以度量从文件开始处计算的字节数。可以调用lseek()函数显示地定位一个打开文件,函数原型为:

#include #include off_t lseek(int fd, off_t offset, int whence);

运行成功返回文件位移量,运行出错返回-1。

测试标准输入能否被设置位移量,offset_test.c:

#include #include #include #include int main(void) { if(lseek(0, 0, SEEK_CUR) == -1) printf("can't seek!\n"); else printf("seek OK!\n"); exit(0); }

编译后执行:

$ ./offset_test can't seek! $ ./offset_test < ./hello.txt seek OK!

可以看出,对于标准输入,一般不能设置位移量,而用户创建的一般文件可以设置位移量。

文件的读写

read()函数

用read()函数从打开文件中读取数据,函数原型为:

#include ssize_t read(int fd, void *buf, size_t count);

运行成功返回读到的字节数,若已到文件尾返回0,运行出错返回-1。

参数fd表示文件描述符,参数buf为指向缓冲区的指针,参数count表示本次操作将要读取的字节数。

实际读取的字节数有时会少于要求读取的字节数:

读普通文件时,在读到要求的字节数之前到达文件尾

从终端设备读时,通常一次读取一行

从网络中读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数

write()函数

用write()函数向打开文件中写数据,函数原型为:

#include ssize_t write(int fd, void *buf, size_t count);

运行成功返回已写的字节数,运行出错返回-1。

参数fd表示文件描述符,参数buf为指向缓冲区的指针,参数count表示本次操作将要写入的字节数。

函数返回值通常与参数count的值相同,否则表示出错。出错的原因通常是磁盘已写满或超过了对一个给定进程的文件长度限制。

使用write函数向文件写入数据,write_file.c:

#include #include #include #include #include #include #include #define FILENAME "./hello.txt" #define SIZE 80 #define FLAGS O_RDWR|O_APPEND int main(void) { int count; int fd; char write_buf[SIZE]; const char *pathname = FILENAME; if((fd=open(pathname, FLAGS)) == -1) { printf("error, open file failed!\n"); exit(1); } printf("OK, open file successful!\n"); printf("Begin Write:\n"); gets(write_buf); count = strlen(write_buf); if(write(fd, write_buf, count) == -1) { printf("error, write file failed!\n"); exit(1); } printf("OK, write %d strings to file!\n", count); return 0; }

编译后执行:

$ ./write_file OK, open file successful! Begin Write: hello,linux C! OK, write 14 strings to file!

查看hello.txt:

$ cat hello.txt hello,linux C!

文件的属性操作

改变文件访问权限

chmod()、fchmod()这两个函数使用户可以更改现存文件的存取许可权:

#include #include int chmod(conts char *pathname, mode_t mode); int fchmod(int fd, mode_t mode);

运行成功返回0,运行出错返回-1。

chmod(),在指定的文件上进行操作,pathname指定了文件的绝对或相对路径名

fchmod(),在已打开的文件上进行操作,fd是文件描述符,mode为文件的权限

使用chmod函数改变文件的访问权限,change_mode.c:

#include #include #include #include #include #define FILENAME "./hello.txt" #define MODE 0700 int main(void) { const char *pathname = FILENAME; if(chmod(pathname, MODE) == -1) { printf("error, change failed!\n"); exit(1); } printf("OK, change successful!\n"); return 0; }

编译后执行:

Linux文件IO操作

$ ./change_mode OK, change successful!

进一步查看该文件的信息:

$ ls -l hello.txt -rwx------ 1 deeplearning deeplearning 14 12月 20 20:09 hello.txt

可以看到文件的权限为rwxr-----,与程序中的MODE宏定义(0755)一致。

改变文件所有者

有3个函数可以改变一个文件的所有者识别号和用户组识别号,函数原型如下:

#include #include int chown(const char *pathname, uid_t owner, git_t group); int fchown(int fd, uid_t owner, git_t group); int lchown(const char *pathname, uid_t owner, git_t group);

运行成功返回0,运行出错返回-1。

chown(),修改指定文件的所有者

fchown(),修改已打开文件的所有者

lchown(),修改符号链接文件本身的所有者

重命名

对文件或目录文件重命名,函数原型:

#include int rename(const char *oldname, const char *newname);

运行成功返回0,运行出错返回-1。

修改文件的长度

截短文件可以调用truncate()和ftruncate(),函数原型如下:

#include #include int truncate(const char *pathname, off_t len); int ftruncate(int fd, off_t len);

运行成功返回0,运行出错返回-1。

文件的其它操作

stat、fsat、lstat函数

Linux系统中所有文件都有一个与之对应的索引节点,该节点包含了文件的相关信息,这些信息被保存在stat结构体中,可以调用下面3个stat函数来返回文件的信息:

#include #include int state(const char *pathname, struct stat *sbuf); int fstate(int fd, struct stat *sbuf); int lstate(const char *pathname, struct stat *sbuf);

运行成功返回0,运行出错返回-1。

stat结构体基本形式如下:

struct stat { mode_t st_mode; // 文件类型&模式 ino_t st_ino; // i-node号 dev_t st_dev; // 设备号 dev_t st_rdev; // 设备号(特殊文件) nlink_t st_nlinkl;// 链接号 uid_t st_uid; // 用户ID gid_t st_gid; // 组ID off_t st_size; time_t st_atime; // access time_t st_mtime; // modification time_t st_ctime; // file station change unsigned long st_blksize; unsigned long st_blocks; }

dup、dup2函数

这两个函数可以用来复制一个现存的文件描述符,原型如下:

#include int dup(int fd); int dup2(int fd, int fd2);

运行成功返回新的文件描述符,运行出错返回-1。

fcntl函数

该函数可以改变已打开文件的性质,原型如下:

#include #include #include int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg);

运行成功依赖于cmd,运行出错返回-1。

sync、fsync函数

为保证磁盘上实际文件系统与缓存中内容的一致性,Linux系统提供了两个系统调用函数,原型如下:

#include void sync(void); int fsync(int fd);

运行成功返回0,运行出错返回-1。

特殊文件的操作

目录文件的操作

mkdir、rmdir函数

mkdir用于创建目录,原型如下:

#include #include int mkdir(const char *pathname, mode_t mode);

运行成功返回0,运行出错返回-1。

rmdir用于删除目录,原型如下:

#include int rmdir(const char *pathname);

运行成功返回0,运行出错返回-1。

opendir 、closedir、readdir函数

opendir用于打开目录,原型如下:

#include #include DIR *opendir(const char *pathname);

运行成功返回指针,运行出错返回NULL。

closedir用于关闭目录,原型如下:

#include #include int closedir(DIR *dp);

运行成功返回0,运行出错返回-1。

readdir用于读取目录,原型如下:

#include #include struct dirent *readdir(DIR *dp);

运行成功返回指针,运行出错返回NULL。

函数返回值指向的结构体指针定义为:

struct dirent { ino_t d_ino; char d_name[NAME_MAX+1]; }

chdir、fchdir、getcwd函数

进程调用chdir或fchdir函数可以更改当前工作目录,函数原型如下:

#include int chdir(const char *pathname); int fchdir(int fd);

运行成功返回0,运行出错返回-1。

这两个函数,可以分别用pathname或文件描述符来指定新的当前工作目录。

获取当前工作目录的绝对路径,使用getcwd函数,函数原型为:

#include char *getcwd(char *buf, size_t size);

运行成功返回buf,运行出错返回NULL。

向此函数传递两个参数,一个是缓存地址buf,另一个是缓存的长度size,该缓存必须有足够的长度以容纳绝对路径名再加上一个NULL终止符,否则返回出错。

改变并获取当前的工作目录,change_path.c:

#include #include #include #include #include #include #define SIZE 30 int main(void) { char newpath[SIZE]; char buf[SIZE]; printf("Input the new pathname[<30 strings]:"); gets(newpath); if(chdir(newpath) == -1) { printf("error, change directory failed!\n"); exit(1); } printf("OK, change directory successful!\n"); if(getcwd(buf, SIZE) == NULL) { printf("error, getcwd failed!\n"); exit(1); } printf("cwd=%s\n", buf); return 0; }

先查看当前的工作目录:

$ pwd /home/deeplearning/dcj/linuxCTest/fileIO

编译后执行:

$ ./change_path Input the new pathname[<30 strings]:/home/deeplearning OK, change directory successful! cwd=/home/deeplearning

再次使用pwd查看当前的工作目录,理论上应该已经切换到新的目录(但是我这里测试没有切换成功,原因未知)。

链接文件的操作

硬链接

创建一个硬链接使用link函数,原型如下:

#include int link(const char *pathname1, const char *pathname2);

运行成功返回0,运行出错返回-1。

此函数创建一个新目录项pathname2,它引用现存文件pathname1,若pathname2已存在则返回出错。

硬链接要求两文件路径位于同一文件系统中,且只有超级用户root才可以创建指向一个目录的新链接。

删除一个硬链接使用unlink函数,原型如下:

#include int unlink(const char *pathname);

运行成功返回0,运行出错返回-1。

也可以使用remove函数解除对一个文件或目录的连接。对于文件,remove的功能与unlink相同,对于目录,remove的功能与rmdir相同。

#include int remove(const char *pathname);

运行成功返回0,运行出错返回-1。

符号链接

符号链接是对一个文件的间接指针。

symlink函数用于创建一个符号链接,原型如下:

#include int symlink(const char *actualpath, const char *sympath);

运行成功返回0,运行出错返回-1。

参考:《精通Linux C编程》- 程国钢

Linux

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【网络通信与信息安全】之深入分析Token、session和cookie的使用场景和区别
下一篇:这一批运维到底在干什么?!
相关文章