信息安全系统设计与实现:第九章学习笔记
信息安全系统设计与实现:第九章学习笔记
20191331 lyx
教材学习内容总结
第九章
本章主要学习目标
通过本章的学习,加深对文件操作理解。掌握I/O库函数及系统调用的使用,理解I/O库函数和系统调用的区别,以及这两种文件操作的优点和不足。了解fread、fwrite和fclose算法与系统调用read、write,close之间的交互。理解I/O操作的不同模式。了解文件缓冲方案。
通过课本的范例代码进行实践,加深对I/O操作的理解和运用。
什么是I/O库函数
功能 | 函数名 | 适用于 |
---|---|---|
字符输入函数 | getchar | 标准输入流 |
字符输出函数 | putchar | 标准输出流 |
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgtes | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | scanf | 标准输入流 |
格式化输出函数 | printf | 标准输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
fclose():文件关闭
函数定义:int fclose(FILE *fp);
函数说明:fp是一个以打开的文件的文件指针
返回值:
正常返回:0
异常返回:EOF,表示文件在关闭时发生错误
fgetc:读取一个字符
函数定义:int fgetc(FILE *fp)
函数说明:从fp中读取一个字符,作为返回值返回
返回值:
正常返回:返回读取字符的代码
异常返回:返回EOF。例如:要从“写打开”的文件中读取一个字符时,会发生错误而返回一个EOF
fputc:写一个字符到文件中
函数定义:int fputc(int ch, FILE*fp)
函数说明:ch是一个整型变量,要写到文件的字符
fp:文件指针,要写入的文件
返回值:
正常返回:要写入的字符的代码
异常返回:返回EOF
fgets():从文件中读取一个字符串
函数定义:char *fgets(char *str, int n, FILE *fp)
函数说明:由fp指出的文件中读取n-1个字符,并把他们存放到有str指出的字符数组中区,最后加上一个由字符串结
束符'\0'
参数说明:str:接受字符串的内存地址,可以是数组别名,也可以是指针
n:指出要读取的字符的个数
fp:这个是文件指针,指出要从中读取字符的文件
返回值:
正常返回:字符串的内存首地址,即str的值
异常返回:返回一个NULL值,此时应当用feof()或ferror()函数来判别是读取到了文件尾,还是发生了错误。
fputs():写入字符串到文件中去
函数定义:把由str之处的字符串写入到fp所指的文件中去
函数说明:
str:之处要写入到文件中去的字符串,不包括最后的'\0'
fp:这个是文件指针,之处字符串要写入到的文件指针
返回值:
正常返回:写入到的文件的字符个数,即字符串的长度
非正常返回:返回一个NULL值,此时应当用feof()或ferror()函数来判别是读取到了文件尾,还是发生了错误。
系统调用
read 系统调用
系统调用 read 的作用是:从文件描述符 fildes 相关联的文件里读入 nbytes 个字节的数据,并把它们放到数据区 buf 中。它返回实际读入的字节数,这可能会小于请求的字节数。如果 read 调用返回 0,就表示没有读入任何数据,已到达了文件尾;如果返回 -1,则表示 read 调用出现了错误。read 系统调用的原型如下:
#include <unistd.h>
size_t read(int fildes,void *buf,size_t nbytes);
write 系统调用
系统调用 write 的作用是把缓冲区 buf 的前 nbytes 个字节写入与文件描述符 fildes 关联的文件中。它返回实际写入的字节数。如果文件描述符有错或者底层的设备驱动程序对数据块长度比较敏感,该返回值可能会小于 nbytes。如果函数返回值为 0,就表示没有写入任何数据;如果返回值为 -1,则表明 write 系统调用出现了错误,错误代码保存在全局变量 errno 里。 write 系统调用的原型如下:
#include <unistd.h>
size_t write(int fildes,const void *buf,size_t nbytes);
close 系统调用
系统调用 close 可以用来终止文件描述符 fildes 与其对应文件之间的关联。当 close 系统调用成功时,返回 0,文件描述符被释放并能够重新使用;调用出错,则返回 -1。
#include <unistd.h>
int close(int fildes);
文件组织参数
文本文件的读取
- 字符读取
字符读取函数 fgetc
fgetc 是 file get char 的缩写,意思是从指定的文件中读取一个字符。它的原型为:
int fgetc (FILE *fp);
fp 为文件指针。fgetc() 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF。
使用举例:
char ch;
FILE *fp = fopen("A.txt", "r+");
ch = fgetc(fp);
- 行读取
字符串读取函数 fgets
fgetc 是 file get string 的缩写,意思是从指定的文件中读取字符串它的原型为:
int fgets (FILE *fp);
使用举例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE 1024
int main()
{
char buf[MAX_LINE]; /*缓冲区*/
FILE *fp; /*文件指针*/
int len; /*行字符个数*/
if((fp = fopen("test.txt","r")) == NULL)
{
perror("fail to read");
exit (1) ;
}
while(fgets(buf,MAX_LINE,fp) != NULL)
{
len = strlen(buf);
buf[len-1] = '\0'; /*去掉换行符*/
printf("%s %d \n",buf,len - 1);
}
return 0;
}
- 任意位置读取
移动文件读写指针
fseek()函数可以移动文件的读写指针到指定的位置,即移动当前文件的位置指针,其原型为:
int fseek(FILE * stream, long offset, int fromwhere);
返回值:成功返回 0,否则返回非 0 值。如果发生读写错误,将会设置设置文件错误标识。
文本文件与二进制文件相互转化
使用vim修改:
vim -b 文件名 ——打开一个二进制文件
:%!xxd ——转化成16进制
:%!xxd -r——转换回二进制
详细解释:https://www.cnblogs.com/killkill/archive/2010/06/23/1763785.html
文件流缓冲理解
我们可以简单的认为系统I/O调用是unbuffered I/O,而C语言标准库I/O函数(即stdio函数)是buffered I/O。
但是需要注意的是:认为使用unbuffered I/O读写磁盘时没有使用缓冲是一个很大的错误认识,实际上,内核是通过高速缓存(kernel buffer cache)来进行实际的磁盘读写的。也就是说,read()和write()系统调用在操作磁盘文件时通常也不会直接发起磁盘访问,而是仅仅在用户空间缓冲区(程序中申请的内存空间)与内核高速缓存之间复制数据。采用这一设计的目的是使read()和write()调用更为快速,减少实际的磁盘访问,因为磁盘访问是很费时的。
而buffered I/O提供的缓冲区工作在用户空间,其存在的目的无疑也是加快程序的效率,主要体现在两个方面
-
减少read()和write()的调用次数 -- 因为频繁地调用系统调用对应用程序来说是很大的效率损失。
-
可以块对齐(block-align)地操作磁盘, -- 因为现在的文件系统和磁盘大都是以“块”为操作单位,所以遵循对齐原则肯定能加速程序速度
因此C语言标准库提供了stdio函数,这些函数提供了stdio缓冲区,那么使用这些函数即提高了程序的效率,又帮助我们免了自行处理数据缓冲的麻烦。
原文章:https://blog.csdn.net/astrotycoon/article/details/44993197
文件操作编程实践
- 实践一
- 程序: File_rw
- 功能: 实现 1.打开指定文件并读取内容 2.在指定文件末尾追加内容
代码:
#include <stdio.h>
#include<string.h>
#include <stdlib.h>
#include <stdbool.h>
FILE *fp;
bool File_check(char *fname)
{
FILE *fp;
_Bool flag;
//if file not exist,exit.
fp = fopen( fname , "r+");
if (fp == NULL)
{
printf("Cannot open the file , press any key to exit!");
getchar();
exit(1);
flag = false;
}
else
{
flag = true;
}
}
int FileR_O(char *fname)
{
char ch;
fp = fopen( fname , "r+");
while ((ch = fgetc(fp)) != EOF)
{
putchar(ch);
}
putchar('\n');
if (ferror(fp))
{
puts("something wrong!");
}
else
{
puts("\nsuccess!");
fclose(fp);
return 0;
}
}
int FileW_I(char *fname)
{
char str[102] = { 0 }, strTemp[100];
fp = fopen( fname , "at+");
printf("Input a string:\n");
scanf("%s", &strTemp);
//while ((ch = getchar()) != '\n')
//gets(strTemp);
strcat(str, "\n");
strcat(str, strTemp);
fputs(str, fp);
fclose(fp);
return 0;
}
int main(int argc, char *argv)
{
char Fname[50];
char fname[]={0};
int choice;
bool flag;
printf("choice to do : 1.file R_O 2.file W_I\n");
scanf("%d", &choice);
printf("write down what file you want : \n");
scanf("%s", &Fname);
sprintf(fname,"%s",Fname);
File_check(fname);
if (flag = true && choice == 1)
{
FileR_O(fname);
}
else if (flag = 1 && choice == 2)
{
FileW_I(fname);
printf("The new file:\n\n");
FileR_O(fname);
}
else
{
return -1;
}
}
在OpenEuler系统下运行使用
- 实践二
- 程序: File_R
- 功能: 以参数形式读取指定文件
- 示例:
./FileR A.txt
代码:
#include <stdio.h>
void main(int argc,char *argv[]) //命令行参数
{
int ch;//定义文件类型指针
FILE *fp;//判断命令行是否正确
if(argc!=2)
{
printf("Error format,Usage: display filename1\n");
return; //键入了错误的命令行,结束程序的执行
}
//按读方式打开由argv[1]指出的文件
if((fp=fopen(argv[1],"r"))==NULL)
{
printf("The file <%s> can not be opened.\n",argv[1]);//打开操作不成功
return;//结束程序的执行
}
//成功打开了argv[1]所指文件
ch=fgetc(fp); //从fp所指文件的当前指针位置读取一个字符
while(ch!=EOF) //判断刚读取的字符是否是文件结束符
{
putchar(ch); //若不是结束符,将它输出到屏幕上显示
ch=fgetc(fp); //继续从fp所指文件中读取下一个字符
} //完成将fp所指文件的内容输出到屏幕上显示
fclose(fp); //关闭fp所指文件
}
在OpenEuler系统下运行使用
- 实践三
- 程序: downtoUP
- 功能: 将指定文件内小写字母转换为大写字母
代码:
#include <stdlib.h>
#include <string.h>
int main ()
{
FILE *fp;
char Filename[50], ch;
int count = 0;
printf("Enter PathName:");
scanf("%s", Filename);
if((fp = fopen(Filename, "r")) == NULL)
{
printf("File open failed!\n");
exit(0);
}
while(!feof(fp))
{
ch = fgetc(fp);
if(ch != EOF)
{
if(ch >= 'a' && ch <= 'z')
{
ch -= 32;
count ++;
}
printf("%c", ch);
}
}
printf("\ntotal change : %d\n", count);
return 0;
}
演示使用
makefile项目管理体验
OpenEuler系统安装CGDB调试工具
在OpenEuler中安装轻量化调试工具CGDB
https://www.cnblogs.com/DKYcaiji/p/15311436.html
总结
文件I/O操作非常基础,也非常实用,很多I/O函数的方法我还没有使用过,但我相信随着程序设计经验的增长,我会掌握这些I/O函数的操作和使用。
I/O函数使用时需注意函数的接口,传入参数类型等,并且作为信息安全专业的学生,我们要特别注意文件流读取时的缓冲区,以及各种极限边界条件,输入输出的合法性等,可以说I/O操作非常重要但又有许多安全隐患,这是我们以后需要努力的方向。
20191331lyx
2021/9/19