标准IO:
1.打开 --- fopen ---> hello.c <---> FILE *fp
2.读写
fgetc/fputc 注: EOF
fgets/fputs
读取结束:
a.size-1
b.\n
c.EOF
buf[] = 字符串 '\0'
fputs
fread/fwrite
3.关闭
fclose
(1).释放堆空间
(2).fflush
4.定位
fseek
rewind
ftell
5.其它
fflush
fprintf
sprintf
perror
feof
ferror
6.缓冲区
行缓存 满 \n fflush 程序正常结束
全缓存 满 fflush 程序正常结束
无缓存
7.练习
touch
cat
copy
文件IO --- 系统调用
1.概念
文件描述符 --- 系统调用这组函数中,对打开文件的 标识
整型数值
0~1023 //1024个
0 -- stdin
1 -- stdout
2 -- stderr
2.基本操作
a.打开 --- open
b.读写 --- read / write
c.关闭 --- close
文件io:
操作系统为了方便用户使用系统功能而对外提供的一组系统函数。
称之为 系统调用
其中有个 文件IO
一般都是对设备文件操作,当然也可以对普通文件进行操作。
一个基于Linux内核的没有缓存的IO机制
------------------------------------------------------------------
文件操作:
缓存 操作对象 具体操作
标准IO 全缓存/行缓存 文件指针(流指针)FILE * 1.打开 --fopen
2.读写
fgetc/fputc
fgets/fputs
fread/fwrite
3.关闭
fclose
4.定位
fseek/ftell/rewind
文件IO 不带缓存 文件描述符 (整数) 1.打开 --open
2.读写 --read/write
3.关闭 --close
4.定位 --lseek
FILE *fp <--操作系统内核([fileno])--->[1.txt]
//文件IO
文件描述符 <----------操作系统--------->[1.txt] //硬盘
stdin --- 0
stdout --- 1
stderr --- 2
----------------------------------------------------------------
特性:
.1 没有缓存区 (//可以认为数据直接交给了内核 )
.2 操作对象不在是流(FILE *),而是文件描述符(整数)
.3文件描述符
很小的非负的整数 int 0-1023
内核每打开一个文件就会获得一个文件 描述符
每个程序在启动的时候操作系统默认为其打开
三个描述符与流对象匹配:
0 ==>STDIN_FILENO === stdin
1 ==>STDOUT_FILENO == stdout
2 ==>STDERR_FILENO == stderr
stdin,stdout,stderr,===>FILE*
库函数:
优点:
a.方便,功能多
b.可移植性好
标准
不足:
c.可能存在 安全性 隐患
系统调用:
优点:
a.使用起来,简单 ,功能简单
b.安全性高
c.设备文件 ---- 文件IO ///dev/video0 ----> fopen();
缺点:
c.很多复杂功能需要自己封装设计
d.可移植性差
fopen("1.txt","r")
{
"r"
open("1.txt",O_RDONLY);
}
标准IO库: 系统调用
fopen open
r O_RDONLY
w O_WRONLY|O_CREAT|O_TRUNC
r+ O_RDWR
w+ O_RDWR |O_CREAT|O_TRUNC
a O_WRONLY|O_CREAT|O_APPEND
a+ O_RDWR |O_CREAT|O_APPEND
O_APPEND
封装
windows NT
ansi c
unistd.h ===>POSIX 标准库
fopen fgetc/s/fread system-v 标准
rw- rw- r--
110 100
6 6 4
fopen open
w O_WRONLY|O_CREAT|O_TRUNC
w+ O_RDWR|O_CREAT|O_TRUNC
r O_RDONLY
r+ O_RDWR
a O_WRONLY|O_CREAT|O_APPEND
a+ O_RDWR|O_CREAT|O_APPEND
open
read /write
close
lseek
库函数 本质上是 对系统调用 的封装
fopen open
r O_RDONLY
r+ O_RDWR
w //文件存在
//文件不存在 fopen(,"w"); ---> open(,O_WRONLY|O_CREAT|O_TRUNC);
//O_WRONLY|O_CREAT|O_TRUNC
w+
//O_RDWR|O_CREAT|O_TRUNC
a //O_WRONLY|O_APPEND
a+ //O_RDWR|O_APPEND
a // O_WRONLY|O_APPEND ==> flags
2.函数接口
1.open
int open(const char *pathname, int flags);
open("1.c",O_WRONLY|O_CREAT,0666 );
int open(const char *pathname, int flags,int mode);
功能:
获得一个文件描述符
参数:
pathname:文件名
flags:
必须项:他们之间是互斥的,所以有且只能有一个
O_RDONLY
O_WRONLY
O_RDWR
可选项:
O_CREAT, 创建文件 //这个标志量存在,则需要指定参数 mode
O_EXCL, 需要和O_CREAT同时使用,表示新建的文件不存在,成功,否则open就会失败
O_TRUNC 文件内容清空
O_APPEND追加
O_RDONLY
O_WRONLY|O_APPEND
fopen()
r ---- O_RDONLY
r+ O_RDWR
w O_WRONLY|O_TRUNC|O_CREAT ,0666
w+ O_RDWR|O_TRUNC|O_CREAT ,0666
a O_WRONLY|O_APPEND|O_CREAT ,0666
a+ O_RDWR|O_APPEND|O_CREAT ,0666
eg:
open("hello.c",O_RDONLY); //fopen("hello.c","r");
fopen ----------------open
"r" O_RDONLY
"r+" O_RDWR
"w" O_WRONLY|O_CREAT |O_TRUNC,0666
"w+" O_RDWR |O_CREAT |O_TRUNC,0666
"a" O_WRONLY|O_APPEND|O_CREAT,0666
"a+" O_RDWR |O_APPEND|O_CREAT,0666
fopen("hello.c","r") //库函数封装系统调用
{
if(strcmp("r",mode) == 0)
{
open("hello.c",O_RDONLY);
}
}
r O_RDONLY
r+ O_RDWR
w O_WRONLY|O_CREAT|O_TRUNC
w+ O_RDWR|O_CREAT|O_TRUNC,0666
a O_WRONLY|O_APPEND|O_CREAT,0666
a+ O_RDWR|O_APPEND|O_CREAT,0666
fopen open
r O_RDONLY
r+ O_RDWR
w O_WRONLY| O_CREAT|O_TRUNC,0666
w+ O_RDWR| O_CREAT|O_TRUNC,0666
a O_WRONLY|O_APPEND|O_CREAT,0666
a+ O_RDWR|O_APPEND|O_CREAT,0666
//后面
O_NOCTTY,不是终端设备
O_ASYNC 异步io,什么时候io不确定,
O_NONBLOCK 非阻塞
open("1.txt",O_WRONLY|O_CREAT,0666);
r w x r w x r w x
1 1 0 1 1 0 1 1 0 666
//0 0 0 0 0 0 0 1 0 umask
1 1 1 1 1 1 1 0 1 ~002 & 1 1 0
---------------------
1 1 0 1 1 0 1 0 0 664
r w x r w x r w x
1 1 1 1 1 1 1 1 1 777
//0 0 0 0 0 0 0 1 0 umask
1 1 1 1 1 1 1 0 1 ~002 & 1 1 0
---------------------
1 1 1 1 1 1 1 0 1 775
最终文件的权限
~umask & 0666
返回值:
成功返回文件描述符 (最近最小未使用)
失败返回-1
练习:
1.用open实现类似touch的命令
#include<stdio.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
int fd=open(argv[1],O_WRONLY|O_CREAT, 0666);
if(fd < 0)
{
perror("open fail");
return -1;
}
printf("fd = %d\n",fd);
return 0;
}
- rw- rw- r--
110 110 100 //664
6 6 4
000 000 010 //002
111 111 101
110 110 110
------------
110 110 100 //664
如果文件存在,则 打印提示
不存在,则创建
O_CREAT|O_EXCL
touch --- 如果文件不存在,则创建该文件(空文件)
如果文件存在,则更新文件的时间戳(makefile)
2.write
char buf[1024];
ssize_t write(int fd, const void *buf, size_t count);
功能:
通过文件描述符向文件中写一串数据
参数:
fd :文件描述符
buf:要写入文件的字符串的首地址
count:要写入字符的个数
返回值:
成功返回 实际写入的个数
失败返回 -1 & errno 被设置
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc, const char *argv[])
{
char buf[100];
while(1)
{
int ret=read(stdin->_fileno,buf,sizeof(buf));
write(1,buf,ret);
}
return 0;
}
3.read
ssize_t read(int fd, void *buf, size_t count);
功能:
通过文件描述符读取文件中的数据
参数:
fd :文件描述符
buf : 存放数据空间的首地址
count:要读到数据的字节个数
返回值:
成功返回读到数据的个数
失败返回-1
读到文件结尾返回0
注意:
read读出来的数据,不是字符串,
如果要输出字符串,需要单独进行处理。
#include<stdio.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
char buf[100];
while(1)
{
int ret = read(stdin->_fileno,buf,sizeof(buf));
buf[ret-1]='\0';
printf("ret = %d,buf=%s\n",ret,buf);
}
return 0;
}
练习:
使用read 实现cat命令
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
int main(int argc, const char *argv[])
{
int fp = open(argv[1],O_RDONLY);
char buf[1024];
int ret = read(fp,buf,1024);
if(ret!=EOF)
{
buf[ret-1]='\0';
printf("%s\n",buf);
}
return 0;
}
基本操作
打开 - open
读写 - read/write
关闭 - close
练习:
用文件IO方式实现文件拷贝
思路:
1.打开
fd_s --src
fd_d --dest
2.读写
3.关闭
close
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
int fp_c=open(argv[1],O_RDONLY);
int fp_d=open(argv[2],O_WRONLY|O_CREAT,0666);
int ret=0;
char buf[1024];
while(ret=read(fp_c,buf,sizeof(buf)))
{
write(fp_d,buf,ret);
}
close(fp_c);
close(fp_d);
return 0;
}
练习:
1、使用以上文件IO方式测试该模式下的操作过程是否真没有缓存
终端上是否可以验证直接输出。
文件中是否可以验证文件直接读写。
2、用以上文件io函数完成一个自己的封装的简单printf函数。
如果有'\n' 则该函数直接输出
如果没有'\n'则缓存并能到达数组满的时候一次输出。
int myprintf(const char * str)
{
char buff[1024] // 缓存
//文件io
}
myprintf("helloworld\n");
hello
world
myprintf("helloworld\n");
sleep(5);
思路:
判断输出的 字符串 有没有 "\n"?
数据写死?
s[strlen(str)-1] 判断该位置是否为 '\n'
是?
write
不是?
buff
判断buff有没有满?
继续等待新的输入
如果满了,则输出 write
作业:
使用文件io方式完成任意两个文件的比较功能,测试两个文件是否相等。
使用read,write复制文件。
fseek ftell
4.lseek //fseek, rewind ftell
off_t lseek(int fd, off_t offset, int whence);
功能:
定位文件的位置
参数:
fd :文件描述符
offset:偏移量
正:向后偏移
负:向前偏移
零:不偏移
whence:
SEEK_SET
SEEK_CUR
SEEK_END
正 空洞
返回值:
成功返回 偏移量
失败返回 -1
lseek(fd,0,SEEK_END);
练习计算文件的大小
off_t len =lseek(fd,0,SEEK_END); //获取到文件的大小
练习:
创建空洞文件
a.先偏移 --lseek
b.写一下 --write
//空洞文件
1.定位
lseek(fd,len,SEEK_SET);
2.写
write(fd,"",1);
练习:
定位到文件开头
lseek(fd,0,SEEK_SET);
获取文件大小:
off_t sizeFile = lseek(fd,0,SEEK_END); //从头到尾的偏移量 就是文件大小
练习:
使用以上lseek函数测试其功能,并验证是否与fseek一样
有部分不支持的特性。
注意:
1、不支持O_APPEND的追加模式,无法生成空洞文件。
2、lseek函数执行失败,文件指针还在偏移前的位置。
3、lseek函数在设备文件上偏移无效。 /dev/video0
4、fifo,socket 也不支持lseek的操作。
空洞文件:
1.偏移
2.写操作
练习:用文件IO的方式
模仿 文件下载
首先创建空洞文件,占用磁盘空间,
然后进行文件的复制。
思路:
1.打开文件
源文件
目标文件
2.读写
从源文件中读取数据
写入到目标文件
3.关闭
--------------------------------------------------
阻塞:程序因为某种条件没有被触发,而导致
'0' '\0'
面试题:
文件IO的文件描述符最大值是多少? ==>1024 个,范围0-1023 《==ulimit -a
--------------------------------------------------------------------
fileno FILE* fp -> int fd
int fileno(FILE *stream);
功能:
获得一个文件流指针中的文件描述符
参数:
stream:文件流指针
返回值:
成功返回文件描述符
失败返回-1
2.fdopen int fd -> FILE *fp
FILE *fdopen(int fd, const char *mode);
功能:
将文件描述符转化为文件流指针
参数:
fd:已经打开的文件描述符
mode:
"r"
"r+"
"w"
"w+"
"a"
"a+"
返回值:
成功返回文件流指针
失败返回NULL
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int lineCnt(FILE *fp)
{
int cnt = 0;
char buf[10];
while (fgets(buf,sizeof(buf),fp) != NULL)
{
if (buf[strlen(buf) - 1] == '\n')
cnt++;
}
return cnt;
}
void do_log(FILE *fp)
{
//1.统计行数
int lines = lineCnt(fp);
//2.输出数据
while (1)
{
time_t t = time(NULL);
struct tm *ptm = localtime(&t);
lines++;
fprintf(stdout,"%d,%04d-%02d-%02d %02d:%02d:%02d\n",lines,ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
fprintf(fp,"%d,%04d-%02d-%02d %02d:%02d:%02d\n",lines,ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
fflush(fp);
sleep(1);
}
}
//./a.out 1.txt
int main(int argc, const char *argv[])
{
if (argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"a+");
if (fp == NULL)
{
perror("fopen fail");
return -1;
}
do_log(fp);
fclose(fp);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int lineCnt(FILE *fp)
{
int cnt = 0;
char buf[10];
while (fgets(buf,sizeof(buf),fp) != NULL)
{
if (buf[strlen(buf) - 1] == '\n')
cnt++;
}
return cnt;
}
void do_log(FILE *fp)
{
//1.统计行数
int lines = lineCnt(fp);
//2.输出数据
while (1)
{
time_t t = time(NULL);
struct tm *ptm = localtime(&t);
lines++;
fprintf(stdout,"%d,%04d-%02d-%02d %02d:%02d:%02d\n",lines,ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
fprintf(fp,"%d,%04d-%02d-%02d %02d:%02d:%02d\n",lines,ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
fflush(fp);
sleep(1);
}
}
//./a.out 1.txt
int main(int argc, const char *argv[])
{
if (argc != 2)
{
printf("Usage: %s <filename>\n",argv[0]);
return -1;
}
FILE *fp = fopen(argv[1],"a+");
if (fp == NULL)
{
perror("fopen fail");
return -1;
}
do_log(fp);
fclose(fp);
return 0;
}
文件IO 练习:
用文件IO方式完成任意文件的拷贝。
文件IO与标准IO的比较:
文件IO 用于底层设备相关的开发,但是
效率和安全性以及移植性没有标准IO方便。
如果是纯上层开发,优先选择使用标准IO 。
作业:
用文件IO 向终端输出100以内所有偶数数字。
标准io 文件io
fopen open
fputc/fgetc write
fputs/fgets read
fwrite/fread
fclose close
fseek lseek
ftell
rewind
fdopen //fd -> FILE*
fileno //FILE* -> fd
//假期作业:
电话簿
tel
struct info
{
char name[256];
char addr[256];
char phone[15];
};
+-----------------------+
|1,add
|2,search
|3.delete
|3.exit
+-----------------------+
1
scanf();
fgets();
switch (chose)
case '1':
add();
break;
case '2':
search();
break;
case 3:
return ;
add()
{
fopen(tel,"a");
fgets(name);
fgets(age);
fgets(phone);
fgets(addr);
fwrite//fputs();
fclose();
}
search()
{
fgets(wantname);
fopen(tel,"r");
fgets(buf);
if(buf.name == wantname)
{
printf(name,age,phone,addr);
}
}
张三 fgets() name;
西安 fgets(); addr,
110 fgets(); phone;
22, int age;
strcat();
张1:西安:110:22
张3:西安:110
张4:西安:110
张5:西安:110
sprintf(buf,"%s:%s:%s:%d\n",name,addr,phone,age);
fprintf(fp,"%s:%s:%s\n",info.name,info.addr,info.phone);
fprintf();
sprintf();
void(*a)(int,int *)
void(*b(int ))(int,int *) ;
int truncate(const char *path, off_t length);
功能:
截断 文件到指定的长度
返回值:
成功 返回 0
失败 -1
-----------------------------------------------------------------
【回顾】
1.stdio ----> file io
stdio file io
操作对象: FILE * int fd
打开 : fopen open //1.txt
读写 : fget c/fputc read
fgets/fputs write
fread/fwrite
关闭 :
fclose close
定位 :
fseek lseek()
ftell
rewind
关联:
fileno fdopen(fd,"r");
FILE * -->fd fd --->FILE *
作业:
1. 使用文件io方式完成任意两个文件的比较功能,测试两个文件是否相等。
fgets(); // FILE *fp --fdopen();
//fd --> FILE *
strcmp(); //string
2. 以文件io形式
模拟迅雷下载文件