目录
(7)定位文件 ftell()/fseek()/rewind()
1.标准IO
1.1.Linux的文件类型
普通文件 - / r
目录文件 d
管道文件 p 在内核创建
字符设备文件 c 每次输入输出是单字节
块设备文件 b 一块数据输入输出
链接文件 l ln -s生成链接文件(软链接)
套接字文件 s 网络通讯
注意:不同的操作系统适用的文件类型不同
1.2.标准IO与文件IO的区别
标准IO遵循ANSI C标准,标准IO实际上是调用标准C库中的函数
特点:
标准IO利用缓冲机制,减少系统调用次数,从而提高程序运行效率
系统调用:不同操作系统接口不一样,通过接口调用硬件,称为系统调用,也就是通过函数来调用
缓冲机制:缓冲区,连续空间,一次把全部需要的全部读取,在内存中存储也就是存储在缓冲区,每次使用时不涉及系统调用
标准IO--高级IO fread 有缓冲区 无实时性
文件IO--低级IO read 无缓冲区 实时性
标准IO是在文件IO的基础上做的一次再封装
有缓冲区无实时性,满足不了一些特殊设备的要求,例如摄像头等
标准IO一般用于操作普通文件
1.3流
数据仅是简单的从文件进行流入和流出,我们把这种现象称为流
流在文件中用,FILE结构体描述。在一个程序中一个文件对应一个FILE结构体
grep -r "_IO_FILE" /usr/include/ 查找FILE结构体
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h
:/ _IO_FILE
struct _IO_FILE
{
int _fileno 文件描述符
char *_IO_buf_base;缓冲区的起始地址
char *_IO_buf_end;缓冲区的末尾地址
...
};
三个特殊的流: 打开文件后,默认打开
标准输入流:-- stdin --键盘设备
标准输出流:--stdout --终端设备
标准错误流:--stderr --特殊终端,输出错误
printf("stdin=%d",stdin->_fileno);//打开一个文件就自己宏定义了stdio
标准输入流 编号为0
标准输出流 编号为1
标准错误流 编号为2
之后的FIlE文件编号从3开始
1.4流的类型
流的类型有两种:文本流、二进制流。
区别:
处理数据的方式不一样。
处理字符数字时不一样。
处理换行符的方式不一
(1)文本流:以字符的方式来处理,将输入输出的数据转换成对应的ASCII码,再进行存取。最后还是转成二进制。
"1234" ----> 49 50 51 52
‘\n’ ---> ‘\r '\n’ (加上\r)(windows)
(2)二进制流:以二进制数字的方式来处理,将数据转换为二进制进行存取
"1234" ----> 看做整数 1234 ,0100 1101 0010
'\n' ---'\n' 换行符不做特殊处理
在linux中,没有对文本流和二进制流作区分,换行符不做特殊处理
打开文件时,可以选择流的类型选择
1.5缓冲区类型
(1)全缓冲:打开一个文件时,默认使用全缓冲,当缓冲区满或空时,才进行IO操作。
满时进行写操作,程序结束时,会把缓冲区刷新(写入),空时进行读操作(数据取完才进行下一次读操作)
(2)行缓冲:当输入输出与终端相关时,使用行缓冲。当缓冲区满(1024)或遇到换行符时,才进行实际的IO操作,程序结束后所有缓冲区刷新
eg:
printf("hello");
while(1);
执行程序时,hello会在行缓冲区中,没打印
(3)无缓冲:与错误输出相关,使用无缓冲(错误需要实时性)(不使用缓冲区),直接输出
2.标准IO相关库函数
fopen()/fclose() --打开/关闭文件
fgetc()/fputc() --按字节读写文件
fgets()/fputs() --按行读写文件
fread()/fwrite() --按对象读写文件
ftell()/fseek()/rewind() --定位文件指针
每打开一个文件就有一个文件指针指向开头
文件指针:定义了读写的位置,每读写一个字符,自动向后偏移1位
fflush() --刷新流,强制刷新缓冲区。提前进行IO
feof()/ferror() --判断错误
man手册:
1.shell指令和可执行程序 ls
2.系统调用函数
3.标准库函
(1)打开文件 --fopen
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);参数:pathname:要打开文件的文件名,字符串型的,没写路径默认当前路径
mode:打开文件的方式(r,r+,w,w+)
r: 以只读的方式打开文件,文件不存在则无法打开
r+: 以读写的方式发开文件,文件不存在则无法打开
w: 以只写的方式打开文件,文件不存在就创建新的
w+: 以读写的方式发开文件,文件不存在就创建新的
a: 以只写,追加的方式(另起一行)打开文件,文件不存在就创建新的
a+:以读写,追加的方式发开文件,文件不存在就创建新的
返回值:成功返回对应的文件的流指针,失败返回NULL,并设置错误号(errno)erron:当前系统中最后一个错误的编号
要显示时加上头文件#include <erron.h>#include <string.h>
char *strerror(int errnum);//打印错误信息
参数: errnum错误号
返回值:错误原因#include <stdio.h>
void perror(const char *s);
参数:s表示提示信息显示结果:提示信息 错误原因
(2)关闭文件 ---fclose()
#include <srdio.h>
int fclose(FILE *stream);
参数: stream甘比文件对应的流指针
返回值:成功返回0,失败返回“EOF”(-1)
程序结束时,程序中打开的文件会自动关闭
(3)按字节读写 fgetc()/fputsc()
#include <stdio.h>
int fgetc(FILE *stream);
参数:stream:FILE流指针
返回值:成功返回获取到的字符,失败返回-1,读到文件末尾也会返回-1.feof()判断错误流。可以区分什么导致的-1.#include <stdio.h>
int fputc(int c, FILE *stream);
参数:c 要写入的字符,stream FILE流指针
返回值:成功返回写入的字符,失败返回-1。练习:1.计算文件大小
2.用fgetc()和fputc()完成文件的拷贝
/*===============================================
* 文件名称:length.c
* 创 建 者:
* 创建日期:2022年08月04日
* 描 述:
================================================*/
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc!=3)
{
printf("Usage:%s <src_filename> <dest_filename>",argv[0]);
return -1;
}
//FILE *fp1=fopen("1.txt","r+"); //打开文件
FILE *fp1=fopen(argv[1],"r+");
if(NULL==fp1)
{
perror("fopen 1");
return -1;
}
//FILE *fp2=fopen("2.txt","w+");
FILE *fp2=fopen(argv[2],"w+"); //打开文件
if(NULL==fp2)
{
perror("fopen 2");
return -1;
}
int i=0,ch;
while((ch=fgetc(fp1))!=-1) //循环中i计数,通过ch一个一个字符复制
{
i++;
fputc(ch,fp2);
printf("%5d",ch);
}
printf("length is %d\n",i);
fclose(fp1);
fclose(fp2);
return 0;
}
(4)按行读写 fgets()/fputs()
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
参数:s 字符串首地址,读取数据到s内。
size 读取数据的最大长度。
stream FILE流
返回值:成功返回读取数据的首地址,失败返回NULL,文件读取完也会是NULL.
注意:1.当读取内容遇到换行符,读取结束
2.最大读取内容为 size-1 ,最后一个字符补上'\0'
3.会把换行符也获取。
#include <stdio.h>
int fputs(const char *s, FILE *stream);
参数:s 字符串首地址,把s字符串写入File流
stream FILE流
返回值:成功返回非零值,失败返回-1
练习:打开一个文件,统计该文件的行数
/*===============================================
* 文件名称:hangshu.c
* 创 建 者:
* 创建日期:2022年08月04日
* 描 述:
================================================*/
#include <stdio.h>
#include <string.h>
#define n 5
int main(int argc, char *argv[])
{
FILE *fp = fopen("1.txt","r+");
if(NULL==fp)
{
perror("fopen");
return -1;
}
char buf[n]={0},i=0;
while(fgets(buf,n,fp)!=NULL ) //循环统计
{
//if(buf[n-2]=='\n'|| strlen(buf)<n-1)
if(buf[strlen(buf)-1]=='\n')//读取完有两种情况,1.没存满,提前遇见\n 2.存满
{
i++;
}
memset(buf,0,n); //以防没有覆盖干净
}
printf("line=%d\n",i);
return 0;
}
(5)按对象读写 fread()/fwrite()
#include <stdio.h>
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);参数:ptr 存放字符串的首地址
size 一个对象大小
nmemb 一次读的对象个数
stream 流指针返回值:成功返回读到的对象的个数(nmemb),失败或读完,返回0。
fwrite(buf, sizeof(char), 1 ,fp);
(6)刷新流---fflush()
#include <stdio.h>
int fflush(FILE *stream);
参数:stream 流指针
返回值,成功返回0,失败返回-1
(7)定位文件 ftell()/fseek()/rewind()
#include <stdio.h>
long ftell(FILE *stream);
功能:返回文件指针相对了文件开头的偏移量
参数:流指针
返回值:return current offset
成功返回当前的偏移量,失败返回-1int fseek(FILE *stream, long offset, int whence);
功能:定位指针,最后的定位位置=offset+whence
参数:stream 流指针
offset 偏移量,可以为负数。
whence 表示基准点,可以设置为以下
SEEK_SET:将文件指针定位在文件开头
SEEK_CUR:将文件指针定位在当前位置
SEEK_END:将文件指针定位在文件末尾
返回值:成功返回0,失败返回-1.
void rewind(FILE *stream);
功能:将文件指针定位在文件开头
参数:流指针
(8)错误判断--feof()
#include <stdio.h>
int feof(FILE *stream);
参数:
返回值:返回1 表示流访问结束(文件读完了)
返回0 表示错误返回
作业:
打开一个文件,向文件中每隔1s记录当前行数和时间
/*===============================================
* 文件名称:homework.c
* 创 建 者:
* 创建日期:2022年08月04日
* 描 述:
================================================*/
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(int argc, char *argv[])
{
int n=0,sec,i; //n
time_t my_t; //为输出时间做准备
time(&my_t);
struct tm *t=localtime(&my_t);
FILE *fp = fopen("1.txt","a+");
char buf[64]={0};
while(fgets(buf,64,fp)!=NULL)//统计有几行
{
if(buf[strlen(buf)-1]=='\n')
{
n++;
}
memset(buf,0,64);
}
sec=t->tm_sec;
while(1)
{
time(&my_t); //为了保证秒钟会变
struct tm *t=localtime(&my_t);
if((sec+1)==t->tm_sec)
{
fprintf(fp,"%3d %d年-%d月-%d日 %d时:%d分:%d秒\n",++n,t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
fflush(fp); //刷新缓冲区,把数据写入文件
sec=t->tm_sec;
if(sec==59)//用于秒数迭代,0~59
{
sec=-1;
}
}
}
return 0;
}
运行结果