【记录】C-文件输入输出
写在开头
摸鱼摸得昏天黑地,是该炸一炸鱼的本愿了~ 以前总觉得笔记没什么重要的,但有时事到临头,又十分渴求简明的提纲来唤起记忆/提供重学的门路。于是就有了以下的产物,也希望能抑制一下无效摸鱼的堕落,以免不久的将来再次被现实逼下马。
正文
C把文件看作是一系列连续的字节。
文件:存储在外部介质(硬盘、U盘、DVD等)上数据的有命名集合,是操作系统数据管理的单位
- 正文文件:文本文件,随字节按照某种字符集(如ASCII、Unicode)进行编码,内容均看作字符
- 二进制文件:数据按照其在内存中的存储形式原样存放
注意不同系统下不同的文件格式
C的文件操作范式
文件类型指针:是一种文件结构体
作业练习而有所得
1. 一些声明
FILE *fp;
char filename[32],s[81],line[1024];
//文件名一般不超过32个字符,显示器上一行通常80个字符,文件最大物理行长度一般为1024
char buf[BUFSIZE];
//在stdio.h中定义,作用如其意,缓冲存储中间处理的字符串
char ch;
2. 养成好习惯,凡是打开文件,便要确认是否成功打开;配上随手关文件
if( (fp=fopen(filename,"r"))==NULL )
{
perror(filename);
return -1;
}
3. fgetc()与fputc()
int fgetc(FILE *stream)
返回所读字符,出错则EOF
int fputc(int c,FILE *stream)
返回所输出字符,出错则EOF
//从一端读取,输入到另外一端,若为stdin/stdout,可改为getchar()与putchar()
while((ch=fgetc(fp))!=EOF)
fputc(ch,fp) //'\n'被同样处理,刚刚好
联合使用,灵活运用返回值
FILE *fin,*fout;
while(!feof(fin))
fputc(fgetc(fin),fout);
4. fopen()中filename的规则
以编译时文件所在目录为起始,格式为路径名称。(“..”表示后从当前目录回退一层)
char filename[]="..\\testdata\\fileio_example.c";
//当前目录回退一层,再进入testdata目录下的文件xxx 同时注意转义字符表示'\'
5. fscanf()与fprintf()
类比sscanf()与sprintf(),字符串改为文件指针
返回值:成功I/O的个数;EOF
6. 对于返回指针的函数:C不支持调用函数时返回局部变量地址
warning:function returns address of local variable
原因:函数调用完后,存储在栈空间的数据会被自动释放,那指针所指的地方就毫无意义了!
解决办法:
①加static
变为局部全局变量,存储在静态区。
②使用malloc()
分配空间,存储在堆空间。
7. 对多维数组的正确理解:以数组为元素的数组
因此,传递参数时的形参不能简单的用多维指针,不要被平时“x维数组”的称呼而轻视了本质
void function(int (*grade)[3][4])//传递一个指向 [3][4]二维数组 的指针
void function(int grade[][3][4]) //也可
void function(int ***grade) //错误!
8. 打表手段'\t'以及认识'\r'
'\t'
:字符长模8后,不足8个字符的用空格凑足8个字符
'\r'
:回车后,内容会被覆盖的
9. fgets()与fputs()
函数原型(注意返回值):
char *fgets(char *str,int size,FILE *stream);
返回指向自身的指针,若没有则为NULL,很自然
int *fputs(char *str,FILE *stream);
返回写入的最后一个字符,出错则为EOF
//作业 fileio_schedule.c
//向reminder.txt文档添加事项,打印已有事项于stdout
#include<stdio.h> //定义了BUFSIZ,本地是512
#include<ctype.h>
#include<stdlib.h>//定义了exit();直接推出进程,似乎正好用于void返回值的函数打开文件失败
void append_reminder()//添加事项
{
FILE *fp;
char buf[BUFSIZ],filename[]="reminder.txt";
int m,d;
if( (fp=fopen(filename,"a"))==NULL )
{
perror(filename);
exit(-1);
}
printf("Enter to add something to your schedule:\n");
while(1)
{
fgets(buf,BUFSIZ,stdin);//fgets()确保最后一位为'\0'的前提后,会把结尾的'\n'读入。即最大读取数为BUFZISIZE-1,如果读入数<=(BUFSIZE-2)就会读入结尾的'\n'
if(buf[0]=='\n') goto A;
if(sscanf(buf,"%d.%d",&m,&d)!=2)//利用sscanf返回读入个数来判断是否按格式输入
{
fputs("Input format:<month>.<day><message>\n",stderr);//区分stderr和stdout
continue;
}
fputs(buf,fp);//与fgets适配
}
A:fclose(fp);
return;
}
void print_schedule()//打印已有事项
{
FILE *fp;
char buf[BUFSIZ];
int m,d;
char message[BUFSIZ]={0};//可以不初始化,因为%s会自动补上'\0' 复习
if( (fp=fopen("reminder.txt","r"))==NULL )
{
perror("reminder.txt");
exit(-2);
}
while(fgets(buf,BUFSIZ,fp)!=NULL)
{
sscanf(buf,"%d.%d%[^\n]",&m,&d,message);
printf("%02d.%02d%s\n",m,d,message);
}
fclose(fp);
return;
}
int main()
{
append_reminder();
print_schedule();
return 0;
}
10. stderr与stdout的理解
stdout有行缓冲,而stderr没有
//trial.c
int main()
{
fprintf(stdout,"first ");
fprintf(stderr,"second");
}
执行时,照理应该是先输出second的,因为stdout行缓冲,遇到'\n'才会从buf中输入到标准输出文件默认设备中。然而本地还是顺序输出的,不知道为何!
但是还是如果给stdout重定向的话,区别就很明显:在命令行中输入 trial.exe > tmp1.txt 再执行,则"first"输入到tmp1.txt中而"second"输入到屏幕上
11. fseek()和ftell()
函数原型:
int fseek(FILE *stream,long offset, int whence)
- 位移量:
offset
表示以起始点为基准,向前向后移动的字节数 (e.g.1L
-2L
ftell(fp)
) - 起始点:
whence
可以是三个符号SEEK_SET
(0)fSEEK_CUR
(1)SEEK_END
(2)
成功则返回非0,出错则非0
函数原型:
long ftell(FILE *stream)
成功则返回相对文件头部的偏移量,出错则-1L
void rewind(FILE *stream)
将文件指针移动到起始,等价于fseek(stream,0,SEEK_SET)
//作业 fileio_len.c 求文件的长度(字节数)-片段
fp=fopen(filename,"rb");//用r打开似乎也一样欸
fseek(fp,0L,SEEK_END);
len=ftell(fp);
printf("Lenth of File is %ld bytes\n",len);
12. 文件存储形式深入理解
//作业 fileio_reverse.c
//将文件按行倒序输出
#include<stdio.h>
#define MAXN 100
//对EOF和fgets以及返回值NULL的理解:fgets读到'\n'或EOF或指定数目时停止,返回所读字符串(也就是说若什么都没读就是NULL)
//EOF只是一个表示文件结束的常量(-1),不是特殊字符
int main()
{
FILE *fp;
char buf[BUFSIZ],filename[]="tmp1.txt";
long offset[MAXN]={0};//标记每行offset的值以便跳转;offset[0]本就为0
int i,j;
//gets(filename);
if((fp=fopen(filename,"r"))==NULL)
{
perror(filename);
return -1;
}
for(i=1;fgets(buf,BUFSIZ,fp)!=NULL;i++) //!执行顺序一定要想清楚!
offset[i]=ftell(fp);//刚好为行首偏移量
//3行的文件,i最后为4,指针指在第5行
for(j=i-2;j>=0;j--)
{
fseek(fp,offset[j],SEEK_SET);
fgets(buf,BUFSIZ,fp);
fputs(buf,stdout);
}
fclose(fp);
}
/*文件存储形式如下 (换行只是为了显示'\n'效果)
abcd\r\n
efghij\r\n
klmnopq\r\n
(EOF)
*/
13. 读/写操作不能连续交替进行,需要“同步文件状态”
//作业 fileio_substitute 将某字符替换成另一字符-片段
while(!feof(fp))
{
if(fgetc(fp)=='*')
{
fseek(fp,-1L,SEEK_CUR);//回退
putc('&',fp);
fseek(fp,ftell(fp),SEEK_SET);//必不可少,同步文件状态
}
}
14. 二进制方式读写相关
应该注意,此时按数据项数计数,而不是 正文模式 中的字节数(char)
常见配套函数:fread()与fwrite()
size_t fwrite(const void *buf, size_t size, size_t count, FILE *stream)
size_t fread (void *buf, size_t size, size_t count, FILE *stream)
需要中间商buf[]
作转接
返回读/写的数据项数(以size
长度为单位),出错则为0
一些操作
//写入数字 3*4=12Byte
int a[3]={23222,1132,128};
fp=fopen("in.dat","wb");
fwrite(a,sizeof(int),3,fp);
fclose(fp);
//整块读取:一次性读入BUFSIZE个整数(512...)
fp=fopen("bin_file.txt","r");
n=fread(buf,sizeof(int),BUFSIZE,fp);
len=ftell(fp);
printf("%d int read,current position is:%d\n",n,len);
//复杂结构的存取
struct d_type{
int size,year,value,group;
double width,height,ratio;
char name[8];
}d_table[MAX_N];
int n;
fwrite(&n,sizeof(int),1,fp_out);//直接取地址,不要忘了哟~
fread(&n,sizeof(int),1,fp_in);
fwrite(d_table,sizeof(struct d_type),n,fp_out);
fread(d_table,sizeof(struct d_type),n,fp_in);
15.文件打开方式备忘录
差不多就粗粗是这些了
【记录】C-文件输入输出的更多相关文章
- Apache日志不记录图片文件设置方法和来源日志的配置
Apache日志不记录图片文件设置方法 <FilesMatch "\.(ico|gif|jpg|swf)">SetEnv IMAG 1</FilesMatch&g ...
- 也用 Log4Net 之将自定义属性记录到文件中 (三)
也用 Log4Net 之将自定义属性记录到文件中 (三) 即解决了将自定义属性记录到数据库之后.一个新的想法冒了出来,自定义属性同样也能记录到文件中吗?答案是肯定的,因为Log4Net既然已经考虑 ...
- C++IO类&文件输入输出
C++IO类&文件输入输出 istream(输入流)类型,提供输入操作. ostream(输出流)类型,提供输出操作. cin,一个istream对象,从标准输入读取数据. cout,一个os ...
- python 中文件输入输出及os模块对文件系统的操作
整理了一下python 中文件的输入输出及主要介绍一些os模块中对文件系统的操作. 文件输入输出 1.内建函数open(file_name,文件打开模式,通用换行符支持),打开文件返回文件对象. 2. ...
- IO库----IO类,文件输入输出,string流
一.IO类 1.IO库类型和头文件表: 头文件 类型 iostream istream,wistream 从流读取数据 ostream,wostream 向流写入数据 iostream,wiostre ...
- Apache 日志设置不记录指定文件类型的方法和日志轮
Apache日志精准的记录了Web访问的记录,但对于访问量很大的站来说,日志文件过大对于分析和保存很不方便.可以在http.conf(或虚拟主机设置文件httpd-vhosts.conf)中进行设置, ...
- 第五次程序设计作业 C++计算器雏形 调用文件输入输出
一.C++计算器作业系列链接 第三次作业:C++计算器雏形 第三次作业附加:代码规范 第四次作业:命令行的调用及计算 MyGithub 二.本次作业相关 要求:第五次程序设计作业 根据这一次的作业要求 ...
- 文件打包代码更新 使用json记录打包文件信息
经过之前的几次试验 决定使用json记录打包文件信息 #include "Package.h" #include "json/json.h" #include ...
- Linux学习-rsyslog.service :记录登录文件的服务
rsyslog.service 的配置文件:/etc/rsyslog.conf 我们现在知道 rsyslogd 可以负责主机产生的各个信息的登录,而这些信息本身是有『严重等级』之分的, 而且, 这些资 ...
- LNMP-Nginx配置不记录静态文件、过期时间
用户访问web网站,通常日志文件会记录很多web站点上的一些静态文件信息,如果长期不处理,日志文件会越来越大,占用的系统资源也越大,此时就需要我们配置不记录静态文件和过期时间,减少日志文件记录过多不必 ...
随机推荐
- MAC brew install 跳过 update
相信很多用 MAC 小伙伴的小伙伴都对 HomeBrew 很熟悉. 但是! 都遇到过这样的问题, 每次安装新东西, 它都要先去 update 一下, 那个耗时啊-. 怎么才能不 update, 直接安 ...
- VS2010/MFC 获取当前程序路径的方法
第一种方法 DWORD GetCurrentDirectory( DWORD nBufferLength, // size, in characters, of directory buffer LP ...
- 阿里IM技术分享(七):闲鱼IM的在线、离线聊天数据同步机制优化实践
本文由阿里闲鱼技术团队书闲分享,原题"如何有效缩短闲鱼消息处理时长",有修订和改动,感谢作者的分享. 1.引言 闲鱼技术团队围绕IM这个技术范畴,已经分享了好几篇实践性总结文章,本 ...
- itextpdf 找出PDF中 文字的坐标
目录 添加引用 添加工具类 调用 找到位置,签名的话见:https://www.cnblogs.com/vipsoft/p/18644127 新项目可以尝试一下 iText 7 , 我这边是老项目所以 ...
- 今天记录一下uniapp制作小程序时包过大的解决方法
在开发小程序的时候,如果业务过于复杂就会使得包太大无法上线,在这我总结了几个解决包过大的方法,避免无法上线 1.静态图片使用线上地址,不要放到项目中,除了navBar的icon,因为那个只能使用本地资 ...
- 《深入理解Mybatis原理》MyBatis数据源与连接池详解
MyBatis数据源DataSource分类 MyBatis把数据源DataSource分为三种: UNPOOLED 不使用连接池的数据源 POOLED 使用连接池的数据源 JNDI 使用JNDI实现 ...
- Solution -「CF 808E」Selling Souvenirs
\(\mathscr{Description}\) Link. 01 背包. 物品种类 \(n\le10^5\),背包容量 \(m\le3\times10^5\),单个物品体积 \(w\i ...
- c# 多线程 lock
模拟10个线程,每个线程模拟100次取钱: 其实就是相当于1000个人来同时取钱.当然实际情况是取钱的人分布在不同的地区的取款机取钱.同一个取款机只能一个人操作. 关键是要保证取钱的余额要准确,不能在 ...
- https证书管理系统- 自动化签发
https证书管理系统- 自动化签发 第一步:前往网站,注册账户 https://www.lingyanspace.com/ 第二步:进入证书服务菜单,点击新增证书 第三步:填写自有的域名,点击创建订 ...
- 「CF 123E」Maze
传送门 题意澄清 对于 dfs 遍历时,在某一个点进入子树的顺序并不是按输入顺序,而是假定随机选择未进入过的子树 (这纠结了我好久) . 破题思路 首先可以明确这题不能推一个 \(O(1)\) 的式子 ...