目录
概念
在系统中定义的一组用于输入输出的函数。
特点
- 没有缓冲机制,每次调用都会引起系统调用
- 围绕文件描述符进行操作,文件描述符是非负整数(>=0),并且是一次分配
- 文件IO默认打开三个文件描述符,分别是0(标准输入),1(标准输出),2(标准错误)
- 可以操作任意类型文件,d除外
标准IO和文件IO权限的区别
标准IO | 文件IO |
r | 只读,文件不存在则报错:O_RDONLY |
r+ | 可读可写,文件不存在则报错:O_RDWR |
w | 只写,文件不存在则创建,存在则清空: O_WRONLY | O_CREAT | O_TRUNC,0666 |
w+ | 可读可写,文件不存在则创建,存在则清空: O_RDWR | O_CREAT | O_TRUNC,0666 |
a | 追加,只写,文件存在则追加,不存在则创建: O_APPEND | O_CREAT | O_WRONLY,0666 |
a+ | 追加,可读可写,文件存在则追加,文件不存在则创建: O_APPEND | O_CREAT | O_RDWR,0666 |
函数
打开文件
open
格式:int open(const char *pathname, int flags);
功能:打开文件
参数:
pathname:文件路径名
flags:打开文件的方式
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_CREAT:创建
O_TRUNC:清空
O_APPEND:追加
返回值:
成功:文件描述符
失败:-1
当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限
int open(const char *pathname, int flags, mode_t mode);
创建出来的文件权限为指定权限值&(~umask) //umask为文件权限掩码
/*
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_APPEND:追加
O_CREAT:创建
O_TRUNC:清空
*/
/*
r:
r+:
w:O_WRONLY|O_CREAT|O_TRUNC,0666;
w+:
a:
a+:
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int fd1 = open("./a.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd1 < 0)
{
perror("open err");
return -1;
}
printf("fd:%d\n", fd1);
close(fd);
return 0;
}
注意:
不同的进程打开相同的文件,文件指针各不相同
关闭文件
close
格式:int close(int fd);
功能:关闭文件
参数:fd:文件描述符
读函数
read
ssize_t read(int fd, void *buf, size_t count);
功能:从一个已打开的可读文件中读取数据
参数:
fd:文件描述符
buf:存放位置
count:期望的个数
返回值:
成功:实际读到的个数
失败:返回-1,并设置errno号
读到文件结尾:返回0
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int fd = open("./a.txt", O_RDWR);
if (fd < 0)
{
perror("open err");
return -1;
}
printf("fd:%d\n", fd);
//读写操作
char buf[32] = {0};
ssize_t s = read(fd, buf, 32);
printf("%d %s\n", s, buf);
write(fd,"nihao",strlen("nihao"));
close(fd);
return 0;
}
写函数
write
格式:ssize_t write(int fd, const void *buf, size_t count);
功能:向指定文件描述符中,写入 count个字节的数据。
参数:
fd:文件描述符
buf:要写的内容
count:期望值
返回值:
成功:实际写入数据的个数
失败:-1
练习1:
用文件IO实现cp功能
命令行参数为:./a.out src dest
/*打开文件 循环读源文件写目标文件,
读到文件末尾循环结束,关闭文件 */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{
ssize_t s = 0;
char buf[32] = {0};
//打开文件
int src = open(argv[1], O_RDONLY);
if (src < 0)
{
perror("open src err");
return -1;
}
int dest = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (dest < 0)
{
perror("open dest err");
return -1;
}
printf("src:%d dest:%d\n", src, dest);
//循环读源文件写目标文件,读到文件末尾循环结束
while ((s = read(src, buf, 32)) != 0)
write(dest, buf, s);
//关闭文件
close(src);
close(dest);
return 0;
}
定位操作
lseek
格式:off_t lseek(int fd, off_t offset, int whence);
功能:设定文件的偏移位置
参数:
fd:文件描述符
offset:偏移量
正数:向文件结尾位置移动
负数:向文件开始位置
whence:相对位置
SEEK_SET 开始位置
SEEK_CUR 当前位置
SEEK_END 结尾位置
返回值:
成功:文件的当前位置
失败:-1
/*向文件中第 10 位置处写一个字符,在文件此时的位置
后 20个位置处,写一行字符串hello进去,
求此时文件的长度。 */
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd = open("./a.txt", O_RDWR);
if (fd < 0)
{
perror("open err");
return -1;
}
printf("fd:%d\n", fd);
lseek(fd, 9, SEEK_SET);
write(fd, "a", 1);
lseek(fd, 20, SEEK_CUR);
write(fd, "hello", 5);
off_t off = lseek(fd, 0, SEEK_END);
printf("len:%ld\n", off);
return 0;
}
练习2:
向文件第10个位置处写一个字符,在文件此时的位置后20个位置处写一行“hello”进去,并求此时文件的长度。
/*向文件中第 10 位置处写一个字符,在文件此时的位置
后 20个位置处,写一行字符串hello进去,
求此时文件的长度。 */
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd = open("./a.txt", O_RDWR);
if (fd < 0)
{
perror("open err");
return -1;
}
printf("fd:%d\n", fd);
lseek(fd, 9, SEEK_SET);
write(fd, "a", 1);
lseek(fd, 20, SEEK_CUR);
write(fd, "hello", 5);
off_t off = lseek(fd, 0, SEEK_END);
printf("len:%ld\n", off);
return 0;
}
标准IO和文件IO的对比
标准IO | 文件IO | |
概念 | C库中定义的一组用于输入输出的函数 | 在系统中定义的一组用于输入输出的函数 |
特点 | 1.有缓冲机制:数据会写到内存缓冲区,伪写入,需要刷新,效率高 2.围绕流来进行操作,FILE * 3.默认打开三个流:stdin、stdout、stderr 4.只能操作普通文件 5.可移植性更强一些 | 1.无缓冲机制:数据会及时冲刷到设备,但是效率低 2.围绕文件描述符操作,非负整数 3.默认打开三个文件描述符:0、1、2 4.可以操作任意类型的文件,除d以外,可操作设备文件 5.在软件设计层面具有可移植性,在Linux中的操作,不能用到Windows系统 |
函数 | 1.打开文件:fopen、freopen 2.关闭文件:fclose 3.读写操作:fgetc/fputc、fgets/fputs、fread/fwrite 4.定位操作:fseek、rewind、ftell 5.刷新函数:fflush | 1.打开文件:open 2.关闭文件:close 3.读写操作:read/write 4.定位操作:lseek |
目录操作
函数
获取目录流:opendir
格式:DIR *opendir(const char *name);
功能:获得目录流
参数:要打开的目录
返回值:
成功:目录流
失败:NULL
读目录:readdir
格式:struct dirent *readdir(DIR *dirp);
功能:读目录
参数:要读的目录流
返回值:
成功:读到的信息
失败:NULL,设置errno号
返回值为结构体,该结构体成员为描述该目录下的文件信息
struct dirent {
ino_t d_ino; /* 索引节点号*/
off_t d_off; /*在目录文件中的偏移*/
unsigned short d_reclen; /* 文件名长度*/
unsigned char d_type; /* 文件类型 */
char d_name[256]; /* 文件名 */
};
关闭目录:closedir
格式:int closedir(DIR *dirp);
功能:关闭目录
参数:dirp:目录流
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char const *argv[])
{
struct dirent *d;
//打开目录
DIR *dir;
dir = opendir(".");
if (dir == NULL)
{
perror("opendir err");
return -1;
}
printf("opendir ok\n");
d = readdir(dir);
printf("%s\n",d->d_name);
//关闭目录流d
closedir(dir);
return 0;
}
练习1:
实现ls功能
//实现ls
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char const *argv[])
{
struct dirent *d;
//打开目录
DIR *dir = opendir(".");
if (dir == NULL)
{
perror("opendir err");
return -1;
}
//读目录
while ((d = readdir(dir)) != NULL)
{
if (d->d_name[0] != '.')
printf("%s ", d->d_name);
}
putchar(10);
//关闭目录
closedir(dir);
return 0;
}
练习2:
实现“head -n 文件名”命令的功能,用atoi函数将字符串转换成整型数
head -n 文件名:显示文件内容
//head -n 文件名
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
char buf[32] = {0};
if (argc != 3)
{
printf("please input %s <-n> <filename>", argv[0]);
return -1;
}
//处理argv[1],获取要显示到第几行
int num = atoi(argv[1] + 1);
//处理argv[2]
//打开文件
FILE *fp = fopen(argv[2], "r");
if (fp == NULL)
{
perror("fopen err");
return -1;
}
//打印前n行
while (fgets(buf, 32, fp) != NULL)
{
if (buf[strlen(buf) - 1] == '\n')
num--;
printf("%s", buf);
if (num == 0)
break;
}
fclose(fp);
return 0;
}
练习3:
编程读写一个文件test.txt,每隔1秒向文件中写入一行数据,类似这样:
1, 2007-7-30 15:16:42
2, 2007-7-30 15:16:43
该程序应该无限循环,直到按Ctrl+C中断程序。
再次启动程序写文件时可以追加到原文件之后,并且序号能够接续上次的序 号,比如:
1, 2007-7-30 15:16:42
2, 2007-7-30 15:16:43
3, 2007-7-30 15:19:02
4, 2007-7-30 15:19:03
5, 2007-7-30 15:19:04
使用到time localtime fprintf sprintf 函数
思路:
- 打开文件,fopen,以a+的形式
- sleep(1)// 延时函数,使进程延时运行,参数是延时的时间
- 计算文件行数
- 计算时间:time,转换成年月日时分秒的形式:localtime
- 写到文件里:fprintf
/*
1.打开文件,fopen,a+
2.sleep(1)
3.计算文件行数
4.计算时间,time,转换成年月日,时分秒localtime
5.写到文件,fprintf
*/
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
time_t t;
struct tm *tm;
int len = 0;
char buf[32] = {0};
//1.打开文件,fopen,a+
FILE *fp = fopen("a.txt", "a+");
if (fp == NULL)
{
perror("fopen err");
return -1;
}
//计算文件行数
while (fgets(buf, 32, fp) != NULL)
{
if (buf[strlen(buf) - 1] == '\n')
len++;
}
//计算时间,time,转换成年月日,时分秒localtime
while (1)
{
time(&t);
tm = localtime(&t);
//写到文件,fprintf
fprintf(fp, "%d,%d-%d-%d %d:%d:%d\n", ++len,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
fflush(NULL);
sleep(1);
}
fclose(fp);
return 0;
}