1、文件IO
①函数原型
- open函数
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode); /* mode 文件权限 */
open函数部分flags分析:
**O_CREAT、O_EXCL:**O_CREAT是创建文件,如果文件已经存在则会重新创建,原来的内容都会被丢弃。而O_EXCL和O_CREAT一起使用的话,如果文件存在,则会返回错误。mode_t mode 这个参数是创建文件时的权限是可以指定的。
如 mode = 666 ; 则代表这个文件的所有者,所属组,其他人都是可读可写(rw),但都没有执行的权限(x).
Linux 系统中,文件的基本权限由 9 个字符组成,以 rwxrw-r-x 为例,我们可以使用数字来代表各个权限,各个权限与数字的对应关系如下:
r --> 4
w --> 2
x --> 1
由于这 9 个字符分属 3 类用户,因此每种用户身份包含 3 个权限(r、w、x),通过将 3 个权限对应的数字累加,最终得到的值即可作为每种用户所具有的权限。
拿 rwxrw-r-x 来说,所有者、所属组和其他人分别对应的权限值为:
所有者 = rwx = 4+2+1 = 7
所属组 = rw- = 4+2 = 6
其他人 = r-x = 4+1 = 5
所以,此权限对应的权限值就是 765。
O_APPEND 、O_TRUNC : O_APPEND是在文件原来内容的后面添加新的内容,O_TRUNC是把文件原来的内容全部丢弃,再写入新的内容。
- read函数
ssize_t read(int fd, void *buf, size_t count);
- write函数
ssize_t write(int fd, const void *buf, size_t count);
- close函数
int close(int fd);
- lseek函数:更改文件指针(光标)
off_t lseek(int fd, off_t offset, int whence);
fd:文件描述符
offset:偏移量
whence:光标的位置 SEEK_SET(文件头)、SEEK_CUR(当前位置)、SEEK_END(文件尾)
/*********************************************************************************
* 文件名:lseek.c
* 功 能:使用lseek()来计算一个文件的长度
* 作 者:FENG
**********************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd = -1; //文件描述符
int ret = -1;
/* 0、判断是否指定文件名 */
if (argc < 2)
{
printf("usage: %s filename!\n", argv[0]);
return -1;
}
/* 1、打开文件 */
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
printf("打开文件失败!\n");
return -1;
}
/* 2、移动文件光标 */
ret = lseek(fd, 0, SEEK_END); //把光标移动到文件末尾
printf("文件长度为:%d\n", ret);
close(fd);
return 0;
}
- dup函数
int dup(int oldfd);
int dup2(int oldfd, int newfd);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int dup3(int oldfd, int newfd, int flags);
②简单使用示例代码
/*********************************************************************************
* 文件名:file.c
* 功 能:文件IO使用示例
* 作 者:FENG
**********************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
int fd = -1; //文件描述符
int ret = -1;
char w_buf[20] = "hello world!";
char r_buf[20] = "";
/* 1、打开文件 */
fd = open("a.txt", O_RDWR | O_CREAT | O_EXCL, 0666); //如果文件不存在就创建,权限为666
if (fd < 0)
{
printf("打开文件失败!\n");
return -1;
}
/* 2、写文件 */
ret = write(fd, w_buf, strlen(w_buf));
if (ret < 0)
{
printf("写入文件失败!\n");
return -1;
}
printf("写入文件成功!共写入%d个字符!\n", ret);
/* 3、读文件 */
//首先,要移动光标到文件开始的位置
lseek(fd, 0, SEEK_SET);
ret = read(fd, r_buf, 20);
if (ret < 0)
{
printf("读出文件失败!\n");
}
printf("读出文件成功!共读出%d个字符!内容为%s\n", ret, r_buf);
/* 4、关闭文件 */
close(fd);
return 0;
}
③文件共享
什么是文件共享?实现文件共享有哪些方式?
文件共享就是一个文件被多个文件描述符同时(一个文件描述符打开但未关闭,另一个文件描述符打开文件)进行读写等操作。
三种方式:
a.同一进程open同一个文件,得到不同的文件描述符指向同一个文件。[O_APPEND可以实现接续写/读文件内容]
b.不同进程open同一个文件,得到不同的文件描述符指向同一个文件。
c.dup和dup2来复制文件描述符,得到不同的文件描述符指向同一个文件。[好用的做法]
/*********************************************************************************
* 文件名:dup.c
* 功 能:复制文件描述符,实现文件共享
* 作 者:FENG
**********************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int fd = -1, cp_fd = -1;
int i = 0;
fd = open("1.txt", O_RDWR | O_CREAT, 0666);
if (fd < 0)
{
printf("文件打开失败!\n");
}
printf("fd = %d\n", fd);
//cp_fd = dup(fd); //复制旧fd,因为close(1),所以1->stdout被释放了,根据Linux自动分配机制,由0开始分配。
cp_fd = dup2(fd, 88); //dup2相比dup可以指定复制的文件描述符的值
printf("cp_fd = %d\n", cp_fd);
for (i = 0; i<10; i++)
{
write(fd, "1111", 4);
write(cp_fd, "2222", 4);
}
close(fd);
return 0;
}
运行的结果:
feng@JFeng:~/linux_app/file_io$ gcc -o dup dup.c
feng@JFeng:~/linux_app/file_io$ ls
2.txt dup dup.c file file.c lseek lseek.c stdout stdout.c
feng@JFeng:~/linux_app/file_io$ ./dup
fd = 3
cp_fd = 88
feng@JFeng:~/linux_app/file_io$ ls
1.txt 2.txt dup dup.c file file.c lseek lseek.c stdout stdout.c
feng@JFeng:~/linux_app/file_io$ cat 1.txt
11112222111122221111222211112222111122221111222211112222111122221111222211112222
feng@JFeng:~/linux_app/file_io$
④重定位标准输出(stdout)
/*********************************************************************************
* 文件名:stdout.c
* 功 能:使用dup()来重定位stdout(标准输出)
* 作 者:FENG
**********************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int fd = -1, cp_fd = -1;
fd = open("1.txt", O_RDWR | O_CREAT, 0666);
if (fd < 0)
{
printf("文件打开失败!\n");
}
printf("fd = %d\n", fd);
close(1); //关闭标准输出设备文件,0->stdin,1->stdout,2->stderr,
cp_fd = dup(fd); //复制旧fd,因为close(1),所以1->stdout被释放了,根据Linux自动分配机制,由0开始分配。
printf("cp_fd = %d\n", cp_fd);
printf("这里已经重定位stdout成功了!\n");
close(fd);
return 0;
}
运行结果:
feng@JFeng:~/linux_app/file_io$ ls
a.txt dup.c file file.c lseek lseek.c stdout.c
feng@JFeng:~/linux_app/file_io$ gcc -o stdout stdout.c
feng@JFeng:~/linux_app/file_io$ ls
a.txt dup.c file file.c lseek lseek.c stdout stdout.c
feng@JFeng:~/linux_app/file_io$ ./stdout
fd = 3
feng@JFeng:~/linux_app/file_io$ ls
1.txt a.txt dup.c file file.c lseek lseek.c stdout stdout.c
feng@JFeng:~/linux_app/file_io$ cat 1.txt
cp_fd = 1
这里已经重定位stdout成功了!
feng@JFeng:~/linux_app/file_io$
命令行重定位,返回信息写入文件中:
feng@JFeng:~/linux_app/file_io$ ls
dup.c file file.c lseek lseek.c stdout stdout.c
feng@JFeng:~/linux_app/file_io$ ls -la > 2.txt
feng@JFeng:~/linux_app/file_io$ ls
2.txt dup.c file file.c lseek lseek.c stdout stdout.c
feng@JFeng:~/linux_app/file_io$ cat 2.txt
total 60
drwxr-xr-x 1 feng feng 4096 Feb 27 00:42 .
drwxr-xr-x 1 feng feng 4096 Feb 26 21:21 ..
-rw-r--r-- 1 feng feng 0 Feb 27 00:42 2.txt
-rw-r--r-- 1 feng feng 0 Feb 27 00:01 dup.c
-rwxr-xr-x 1 feng feng 8656 Feb 26 22:33 file
-rw-r--r-- 1 feng feng 1351 Feb 27 00:01 file.c
-rwxr-xr-x 1 feng feng 8472 Feb 26 22:27 lseek
-rw-r--r-- 1 feng feng 950 Feb 26 22:33 lseek.c
-rwxr-xr-x 1 feng feng 8472 Feb 27 00:25 stdout
-rw-r--r-- 1 feng feng 902 Feb 27 00:25 stdout.c
feng@JFeng:~/linux_app/file_io$
⑤标准IO
-
标准IO库函数与文件IO(API)有什么不同?标准IO更具有移植性,其实就是在文件IO的基础上再做了一层封装,效率更高,易用性更好。
-
函数原型
FILE *fopen(const char *pathname, const char *mode); size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream); int fclose(FILE *stream); int fseek(FILE *stream, long offset, int whence);
-
一些对比
fopen() mode open() flags r O_RDONLY w O_WRONLY | O_CREAT | O_TRUNC a O_WRONLY | O_CREAT | O_APPEND r+ O_RDWR w+ O_RDWR | O_CREAT | O_TRUNC a+ O_RDWR | O_CREAT | O_APPEND
/*********************************************************************************
* 文件名:std_io.c
* 功 能:标准IO库函数使用示例
* 作 者:FENG
**********************************************************************************/
#include <stdio.h>
int main(void)
{
FILE *fp = NULL;
size_t len = -1;
char w_buf[20] = "hello world!";
char r_buf[20] = {0};
/* 1、打开文件 */
fp = fopen("a.txt", "w+");
if (fp == NULL)
{
printf("打开文件失败!\n");
return -1;
}
/* 2、写文件 */
len = fwrite(w_buf, sizeof(char), sizeof(w_buf)/sizeof(w_buf[0]), fp);
if (len < 0)
{
printf("写文件失败!\n");
}
printf("写文件成功,写入%ld字节,内容是%s\n", len, w_buf);
/* 3、读文件 */
//首先,要移动一下光标
fseek(fp, 0, SEEK_SET);
len = fread(r_buf, sizeof(char), sizeof(r_buf)/sizeof(r_buf[0]), fp);
if (len < 0)
{
printf("读文件失败!\n");
}
printf("读文件成功,读出%ld字节,内容是%s\n", len, r_buf);
/* 4、打开文件 */
fclose(fp);
return 0;
}
⑥补充说明
- 文件描述符:文件描述符就是由Linux系统自动分配的,既然是系统分配的那就遵循一定的规律,其实文件描述符是由数组来进行管理的文件描述符表,inode就是描述符指针,value就是那个数值。其实0,1,2已经被系统使用,分别对应stdin、stdout、stderr。我们使用open打开的最小的文件描述符是3。
- man手册:可以查询命令、API、函数 详细信息。
- man 1 命令 :查询命令 man 1 ls
- man 2 API : 查询API 如 man 2 open
- man 3 函数 :查询函数 如man 3 memset