对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传递给read或write
文件描述符的变化范围是0 ~ OPEN_MAX - 1
注意:
文件描述符 0:标准输入(STDIN_FILENO)
文件描述符 1:标准输出(STDOUT_FILENO)
文件描述符 2:标准错误(STDERR_FILENO)
调用open函数可以打开或创建一个文件
#include
#include
#include
int open(const char *path, int oflag,mode_t mode);
参数:
path: 是要打开或创建文件的名字(包含路径)
oflag:可用来说明此函数的多个选项
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 可读、写打开
O_EXEC 只执行打开
O_SEARCH 只搜索打开(应用于目录)
以上5个常量中必须指定一个且只能指定一个,以下常量则是可选
O_APPEND 每次写时都追加到文件的尾端
O_CLOEXEC 把FD_CLOEXEC常量设置为文件描述符标志
O_CREAT 若文件不存在则创建它
O_DIRECTORY 如果path引用的不是目录,则出错
O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错
O_NOCTTY 如果path引用的是终端设备,则不将该设备分配作为此进程的控制终端
O_NOFOLLON 如果path引用的是一个符号链接,则出错
O_NONBLOCK 如果path引用的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本次打开操作和后续的I/O操作设置非阻塞方式
O_SYNC 使每次write等待物理I/O操作完成,包括由该write操作引起的文件属性更新所需的I/O
O_TRUNC 如果此文件存在,而且为只读或读-写成功打开,则将其长度截断为0
O_TTY_INIT 如果打开一个还未打开的终端设备,设置非标准termios参数值,使其符合Single UNLX Specification
mode:记录待创建的文件的访问权限
返回值:若成功,返回文件描述符;若失败返回-1
调用creat函数创建一个文件
#include
#include
#include
int creat(const char *pathname, mode_t mode);
参数:
pathname:创建文件的名字(包含路径)
mode: 记录待创建的文件的访问权限
返回值:若成功,返回为只写打开的文件描述符;若出错,返回-1
注意:此函数等效于:open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
creat 的一个不足之处是它以只写方式打开所创建的文件。在提供open的新版本之前,如果要创建一个临时文件,并要先写该文件,然后又读该文件,则必须先调用creat、close,然后再调用open。现在则可用下列方式调用open实现:
open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
可调用close函数关闭一个打开文件
#include
int close(int fd);
参数:
fd:是指需要关闭文件的描述符
返回值:若成功,返回0;若出错,返回-1
关闭一个文件时还会释放该进程加在该文件上的所有记录锁
每个打开文件都有一个与其相关联的“当前文件偏移量”(current file offset)。它通常是一个非负整数,用以度量从文件开始处计算的字节数。
通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。按系统默认的情况,当打开一个文件时,除非指定 O_APPEND 选项,否则该偏移量被设置为0
#include
#include
off_t lseek(int fd, off_t offset, int whence);
参数:
fd: 文件描述符
offset:偏移值
whence:
若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处 offset 个字节
若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正或负
若whence是SEEK_END,则将该文件的偏移量设置为文件的长度加offset,offset可正可负
返回值:若成功,返回新的文件偏移量;若出错,返回-1
获取当前偏移量的位置
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR); // 表示从当前位置偏移0个字节,并返回偏移量
这种方法可用来确定所涉及的文件是否可以设置偏移量。如果文件描述符指向的是一个管道、FIFO或网络套接字,则lseek返回-1,并将errno设置为ESPIPE
调用read函数从打开文件中读取数据
#include
ssize_t read(int fd, void *buf, size_t count);
参数:
fd: 文件描述符
buf: 将读取到的数据存入buf缓冲区中
count:读取的字节数
返回值:读到的字节数,若已到文件尾,返回0;若出错,返回-1
理论上read的返回值和count的大小一样,但也有读取到的字节数小于count
调用write函数向打开文件写入数据
#include
ssize_t write(int fd, const void *buf, size_t count);
参数:
fd: 文件描述符
buf: 写入缓冲区,将需要写入文件的内容存入buf中
count: 写入的字节数
返回值:若成功,返回已写的字节数;若出错,返回-1
其返回值通常与参数count的值相同,否则表示出错。write出错的一个常见原因是磁盘已写满,或者超过了一个给定进程的文件长度限制。
对于普通文件,写操作从文件的当前偏移量处开始。如果在打开该文件时,指定了 O_APPEND 选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处。在一次成功写之后,该文件偏移量增加实际写的字节数。
FILE *fopen(char *filename, *type);
参数:
filename: 文件名称
mode: 打开模式:
r 只读方式打开一个文本文件
rb 只读方式打开一个二进制文件
w 只写方式打开一个文本文件
wb 只写方式打开一个二进制文件
a 追加方式打开一个文本文件
ab 追加方式打开一个二进制文件
r+ 可读可写方式打开一个文本文件
rb+ 可读可写方式打开一个二进制文件
w+ 可读可写方式创建一个文本文件
wb+ 可读可写方式生成一个二进制文件
a+ 可读可写追加方式打开一个文本文件
ab+ 可读可写方式追加一个二进制文件
返回值:返回一个文件指针
open和fopen的区别:
1 、打开一个文件,如果文件不存在则创建
#include
#include
#include
#include
int main()
{
int fd;
// 如果file1文件不存在则会创建file1文件,如果存在open函数会返回-1
fd = open("./file1", O_RDWR | O_CREAT | O_EXCL, 0x600); //0600 代表可读可写权限
if(fd == -1) // O_EXCL 文件存在则返回-1
printf("file exist!!!\n");
else
printf("file creat succeed!!!\n");
return 0;
}
2、在文件末尾添加数据,不改变原本内容!!!
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
char *buf = "welcom to Linux !!!";
/*
O_RDWR 是从文件的开头开始写入,并覆盖原有的内容
O_APPEND 在文件末尾处写入,不改变原有内容
O_TRUNC 在清除文件的数据,并头开始写入
*/
fd = open("./file1", O_RDWR | O_APPEND); // 可读可写,光标移动至尾端
printf("open file success!!!\n");
int n_write = write(fd, buf, strlen(buf));
if(n_write != -1)
printf("write %d byte to file\n", n_write);
close(fd);
return 0;
}
3、创建一个文件
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
char *buf = "Hello Linux World";
fd = creat("./file2", S_IRWXU); // 创建文件,权限是可读可写可执行
/**************************************
*常见创建模式(mode): vi
* 宏表示 数字
* S_IRUSR 4 可读
* S_IWUSR 2 可写
* S_IXUSR 1 可执行
* S_IRWXU 7 可读可写可执行
**/
write(fd, buf, strlen(buf)); // 写入文件
return 0;
}
4、打开text文件,若没有该文件则创建。 之后在text中写入数据,最后在读取出来显示在屏幕并计算数据大小。
#include
#include
#include
#include
#include
#include
#include
int
main()
{
int fd;
char *Write_buf = "Welcom to Linuix\n";
fd = open("./test", O_RDWR | O_APPEND); // 写入的内容在尾端直接添加
if(fd == -1)
{
printf("open file fiale!!!\n");
fd = open("./test", O_RDWR | O_CREAT | O_APPEND, 0600);
if(fd > 0)
printf("creat file success!!!\n");
else
{
printf("creat file faile!!!\n");
exit(0);
}
}
int byte = write(fd, Write_buf, strlen(Write_buf)); // 写入的大小,单位字节
printf("write file %d byte\n", byte);
char *read_buf = (char *)malloc(sizeof(char) * byte); // 申请空间
// 由于偏移量在尾端,需要往前移动,具体移动多少?
lseek(fd, -byte, SEEK_END); // 在末尾写入多少我们就往前移动多少,最后读取写入的内容
byte = read(fd, read_buf, strlen(Write_buf));
printf("read:%s\nread Byte:%d\n",read_buf, byte); // 输出写入的内容
close(fd); // 一定要记得关闭
return 0;
}
5、获取一个文件中内容的字节数
#include
#include
#include
#include
#include
#include
#include
int
main()
{
int fd;
fd=open("test",O_RDWR);
char *write_buf="hello linux!";
if(fd==-1)
{
printf("no file\n");
fd=open("test",O_RDWR|O_CREAT,0600);
if(fd > 0)
printf("file creat success\n");
else if(fd == -1)
{
printf("file creat failt! \n");
exit(0);
}
}
printf("fd=%d\n",fd);
int size=lseek(fd,0,SEEK_END); // 获取字节数
printf("file size=%d\n",size);
close(fd);
return 0;
}
6、实现复制文件cp指令
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int fdSrc;
int fdDes;
char *readBuf = NULL; // 定义野指针
if(argc != 3)
{
printf("pararn error!!!\n");
exit(-1);
}
fdSrc = open(argv[1], O_RDWR);
int size = lseek(fdSrc, 0, SEEK_END); // 计算字节大小
lseek(fdSrc, 0, SEEK_SET); // 把光标移动至开头
// 申请空间内存
readBuf = (char *)malloc(sizeof(char) * size + 8); // 加上8个字节为了防止溢出的可能
int n_read = read(fdSrc, readBuf, size); // 有多少字节读取多少字节
// O_TRUNC 的存在使得先清空再写入数据
fdDes = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0600);
int n_write = write(fdDes, readBuf, strlen(readBuf));
close(fdSrc);
close(fdDes);
return 0;
}
7、从键盘上获取数据,并输出数据
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
char readBuf[128];
/*
0 标准输入
1 标准输出
2 标准错误
*/
// 读取键盘的输入
int n_read = read(0, readBuf, 10); // 标准输入
int n_write = write(1, readBuf, strlen(readBuf));
return 0;
}
8、向file1文件中添加整型数,并输出写入文件中的数字
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
int data = 100;
int data2 = 0;
fd = open("./file1", O_RDWR);
int n_write = write(fd, &data, sizeof(int));
lseek(fd, 0, SEEK_SET);
int n_read = read(fd, &data2, sizeof(int));
printf("read %d \n", data2);
close(fd);
return 0;
}
9、向文件中写入一个结构体
#include
#include
#include
#include
#include
#include
#include
typedef struct
{
int a;
char c;
}Data;
int main()
{
int fd;
/*定义结构体*/
Data data = {100, 'c'};
Data data2;
fd = open("./file1", O_RDWR);
// 将data数据写入file1文件中
int n_write = write(fd, &data, sizeof(Data));
// 光标回到开头
lseek(fd, 0, SEEK_SET);
// 将file1中的数据读取到data2中
int n_read = read(fd, &data2, sizeof(Data));
// 输出data2的数据
printf("read %d,%c \n", data2.a, data2.c);
close(fd);
return 0;
}
10、fopen函数的用法
#include
#include
#include
int main()
{
FILE *fp;
char *str = "welcom to LINUX!!!";
char *readBuf = NULL; // 定义野指针
fp = fopen("./jian.txt","w+");
// size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
/**
* 参数:
* *ptr 代表 缓冲区地址
* size 代表 写入长度
* nmemb 代表 写入字节的长度
* stream 代表 哪个文件
*/
fwrite(str, sizeof(char), strlen(str), fp); // 写入数据(光标在末尾)
// fwrite(str, sizeof(char)*strlen(str), 1, fp);
int size = fseek(fp, 0, SEEK_END); // 获取长度
fseek(fp, 0, SEEK_SET); // 移动光标至开头
readBuf = (char *)malloc(sizeof(char)*size + 8); // 申请空间内存
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread(readBuf, sizeof(char), strlen(str), fp); // 读取写入的数据
printf("read data: %s\n", readBuf); // 输出读取到的数据
return 0;
}
C程序总是从main函数开始执行。
main函数的原型是:
int main(int argc, char *argv[]);
或
int main(int argc, char **argv);
其中,argc是命令行参数的数目,argv是指向参数的各个指针所构成的数组。
有8种方式使进程停止(termination),其中5种为正常终止
异常终止:
3个函数用于正常终止一个程序: _exit 和 _Exit 立即进入内核,exit 则先执行一些清理处理,然后返回内核
#include "stdlin.h"
void exit(int status);
void _Exit(int status);
#include "unistd.h"
void _exit(int status);
exit()用来正常终结目前进程的执行,并把参数status返回给父进程,
而进程所有的缓冲区数据会自动写回并关闭未关闭的文件。
mian函数返回一个整型值与该值调用exit是等价的
exit(0);
等价于
return 0;