自己动手写操作系统:2.C语言文件操作,制作系统引导程序
来源:互联网 发布:华方 线切割编程 编辑:程序博客网 时间:2024/06/09 22:30
本次学习的目标如下,我们带着目标来学习,这样效果更好。
目标:1.把 A文件中的内容 读出并写入到 B 文件中,且在B文件的511字节和512字节赋值 0x55 0xaa
目标:2.指定A的内容:由汇编程序生成,写入到B中,且在B文件的511字节和512字节赋值 0x55 0xaa
开始学习:
一、c语言文件的操作模式:
r:可读
w:可写
a:在文件后面追加
b:二进制模式
+:可读可写权限
r:可读
w:可写
a:在文件后面追加
b:二进制模式
+:可读可写权限
"r" 打开,只读;
"w" 打开,文件指针指到头,只写;
"a" 打开,指向文件尾,在已存在文件中追加;
"rb" 打开一个二进制文件,只读;
"wb" 打开一个二进制文件,只写;
"ab" 打开一个二进制文件,进行追加 ;
"r+" 以读/写方式打开一个已存在的文件;
"w+" 以读/写方式建立一个新的文本文件 ;
"a+" 以读/写方式打开一个文件文件进行追加 ;
"rb+" 以读/写方式打开一个二进制文件;
"wb+" 以读/写方式建立一个新的二进制文件 ;
"ab+" 以读/写方式打开一个二进制文件进行追加
打开文件
FILE *fp; if((fp=fopen("test","w"))==NULL) { printf("File cannot be opened/n"); exit(); } else { printf("File opened for writing/n"); }
关闭文件
if(fclose(fp)!=0) { printf("File cannot be closed/n"); exit(1); } else { printf("File is now closed/n"); }
它表示该函数将关闭FILE指针对应的文件,并返回一个整数值。
若成功地关闭了文件,则返回一个0值,否则返回一个非0值
int fcloseall(); //关闭全部打开的文件
该函数将关闭所有已打开的文件,将各文件缓冲区未装满的内容写到相应的文件中去,接着释放这些缓冲区,并返回关闭文件的数目。
如关闭了4个文件,则当执行: n=fcloseall(); 时,n应为4。
二、文件的读写
1.一次只读写文件中的一个字符
int fgetc(FILE *stream);//读完返回-1
int getchar(void);
该函数将关闭所有已打开的文件,将各文件缓冲区未装满的内容写到相应的文件中去,接着释放这些缓冲区,并返回关闭文件的数目。
如关闭了4个文件,则当执行: n=fcloseall(); 时,n应为4。
二、文件的读写
1.一次只读写文件中的一个字符
int fgetc(FILE *stream);//读完返回-1
int getchar(void);
int fputc(int ch,FILE *stream);
int putchar(int ch);
int putchar(int ch);
int getc(FILE *stream);
int putc(int ch,FILE *stream);
int putc(int ch,FILE *stream);
注:为什么不适用char ch 而使用 int ch
注意,这里使用char ch,其实是不科学的,因为最后判断结束标志时,
是看ch!=EOF,而EOF的值为-1,这显然和char是不能比较的。
注意,这里使用char ch,其实是不科学的,因为最后判断结束标志时,
是看ch!=EOF,而EOF的值为-1,这显然和char是不能比较的。
#include "stdio.h" #include <stdlib.h> int main() { FILE *fp; char ch; if((fp=fopen("myfile.txt","r"))==NULL) { printf("file cannot be opened/n"); exit(1); } while((ch=fgetc(fp))!=EOF) fputc(ch,stdout); fclose(fp); }
2、读写文件中字符串的函数
(1) char *fgets(char *string,int n,FILE *stream);
其中fgets()函数将把由流指针指定的文件中n-1个字符,读到由指针string指向的字符数组中去
fgets()函数读到'/n'就停止,而不管是否达到数目要求。同时在读取字符串的最后加上'/0'。 fgets()函数执行完以后,返回一个指向该串的指针。如果读到文件尾或出错,则均返回一个空指针NULL,
所以常用feof()函数来测定是否到了文件尾或者是用ferror()函数来测试是否出错,
例如下面的程序用fgets()函数读test.txt文件中的第一行并显示出来:
(1) char *fgets(char *string,int n,FILE *stream);
其中fgets()函数将把由流指针指定的文件中n-1个字符,读到由指针string指向的字符数组中去
fgets()函数读到'/n'就停止,而不管是否达到数目要求。同时在读取字符串的最后加上'/0'。 fgets()函数执行完以后,返回一个指向该串的指针。如果读到文件尾或出错,则均返回一个空指针NULL,
所以常用feof()函数来测定是否到了文件尾或者是用ferror()函数来测试是否出错,
例如下面的程序用fgets()函数读test.txt文件中的第一行并显示出来:
#include "stdio.h" int main() { FILE *fp; char str[128]; if((fp=fopen("test.txt","r"))==NULL) { printf("cannot open file/n"); exit(1); } while(!feof(fp)) { if(fgets(str,128,fp)!=NULL) printf("%s",str); } fclose(fp); } (2) char *gets(char *s); gets()函数执行时,只要未遇到换行符或文件结束标志,将一直读下去。因此读到什么时候为止,需要用户进行控制,否则可能造成存储区的溢出。
(3) int fputs(char *string,FILE *stream);
fputs()函数向指定文件写入一个由string指向的字符串,'/0'不写入文件。
fputs()函数向指定文件写入一个由string指向的字符串,'/0'不写入文件。
(4) int fprintf(FILE *stream,char *format,variable-list);
(5) int fscanf(FILE *stream,char *format,variable-list);
fprintf()和fscanf()同printf()和scanf()函数类似,不同之处就是printf()函数是想显示器输出,
fprintf()则是向流指针指向的文件输出;fscanf()是从文件输入。 可以用来进行格式转化
(5) int fscanf(FILE *stream,char *format,variable-list);
fprintf()和fscanf()同printf()和scanf()函数类似,不同之处就是printf()函数是想显示器输出,
fprintf()则是向流指针指向的文件输出;fscanf()是从文件输入。 可以用来进行格式转化
三、清除和设置文件缓冲区
int fflush(FILE *stream);
int flushall();
fflush()函数将清除由stream指向的文件缓冲区里的内容,常用于写完一些数据后,立即用该函数清除缓冲区,以免误操作时,破坏原来的数据。
flushall()将清除所有打开文件所对应的文件缓冲区。
int fflush(FILE *stream);
int flushall();
fflush()函数将清除由stream指向的文件缓冲区里的内容,常用于写完一些数据后,立即用该函数清除缓冲区,以免误操作时,破坏原来的数据。
flushall()将清除所有打开文件所对应的文件缓冲区。
(2).设置文件缓冲区函数
void setbuf(FILE *stream,char *buf);
void setvbuf(FILE *stream,char *buf,int type,unsigned size);
这两个函数将使得打开文件后,用户可建立自己的文件缓冲区,而不使用fopen()函数打开文件设定的默认缓冲区。 对于setbuf()函数,buf指出的缓冲区长度由头文件stdio.h中定义的宏BUFSIZE的值决定,缺省值为512字节。
当选定buf为空时,setbuf函数将使的文件I/O不带缓冲。
而对setvbuf函数,则由malloc函数来分配缓冲区。参数size指明了缓冲区的长度(必须大于0),而参数type则表示了缓冲的类型,其值可以取如下值: type 值 含义 _IOFBF 文件全部缓冲,
即缓冲区装满后,才能对文件读写 _IOLBF 文件行缓冲,即缓冲区接收到一个换行符时,才能对文件读写 _IONBF 文件不缓冲,此时忽略buf,size的值,直接读写文件,不再经过文件缓冲区缓冲。
void setbuf(FILE *stream,char *buf);
void setvbuf(FILE *stream,char *buf,int type,unsigned size);
这两个函数将使得打开文件后,用户可建立自己的文件缓冲区,而不使用fopen()函数打开文件设定的默认缓冲区。 对于setbuf()函数,buf指出的缓冲区长度由头文件stdio.h中定义的宏BUFSIZE的值决定,缺省值为512字节。
当选定buf为空时,setbuf函数将使的文件I/O不带缓冲。
而对setvbuf函数,则由malloc函数来分配缓冲区。参数size指明了缓冲区的长度(必须大于0),而参数type则表示了缓冲的类型,其值可以取如下值: type 值 含义 _IOFBF 文件全部缓冲,
即缓冲区装满后,才能对文件读写 _IOLBF 文件行缓冲,即缓冲区接收到一个换行符时,才能对文件读写 _IONBF 文件不缓冲,此时忽略buf,size的值,直接读写文件,不再经过文件缓冲区缓冲。
四、文件的随机读写函数
(1).移动文件指针函数:
long ftell(FILE *stream);
int rewind(FILE *stream);
fseek(FILE *stream,long offset,int origin);
函数ftell()用来得到文件指针离文件开头的偏移量。当返回值是-1时表示出错。 rewind()函数用于文件指针移到文件的开头,当移动成功时,返回0,否则返回一个非0值。 fseek()函数用于把文件指针以origin为起点移动offset个字节,其中origin指出的位置可有以下几种:
origin 数值 代表的具体位置
SEEK_SET 0 文件开头
SEEK_CUR 1 文件指针当前位置
SEEK_END 2 文件尾
例如: fseek(fp,10L,0); 把文件指针从文件开头移到第10字节处,由于offset参数要求是长整型数,故其数后带L。
fseek(fp,-15L,2); 把文件指针从文件尾向前移动15字节。
(1).移动文件指针函数:
long ftell(FILE *stream);
int rewind(FILE *stream);
fseek(FILE *stream,long offset,int origin);
函数ftell()用来得到文件指针离文件开头的偏移量。当返回值是-1时表示出错。 rewind()函数用于文件指针移到文件的开头,当移动成功时,返回0,否则返回一个非0值。 fseek()函数用于把文件指针以origin为起点移动offset个字节,其中origin指出的位置可有以下几种:
origin 数值 代表的具体位置
SEEK_SET 0 文件开头
SEEK_CUR 1 文件指针当前位置
SEEK_END 2 文件尾
例如: fseek(fp,10L,0); 把文件指针从文件开头移到第10字节处,由于offset参数要求是长整型数,故其数后带L。
fseek(fp,-15L,2); 把文件指针从文件尾向前移动15字节。
(2).文件随机读写函数
int fread(void *ptr,int size,int nitems,FILE *stream);
int fwrite(void *ptr,int size,int nitems,FILE *stream);
fread()函数从流指针指定的文件中读取nitems个数据项,每个数据项的长度为size个字节,读取的nitems数据项存入由ptr指针指向的内存缓冲区中,
在执行fread()函数时,文件指针随着读取的字节数而向后移动,最后移动结束的位置等于实际读出的字节数。
该函数执行结束后,将返回实际读出的数据项数,这个数据项数不一定等于设置的nitems,因为若文件中没有足够的数据项,或读中间出错,都会导致返回的数据项数少于设置的nitems。
当返回数不等于nitems时,可以用feof()或ferror()函数进行检查。 fwrite()函数从ptr指向的缓冲区中取出长度为size字节的nitems个数据项,写入到流指针stream指向的文件中,执行该操作后,文件指针将向后移动,移动的字节数等于写入文件的字节数目。
该函数操作完成后,也将返回写入的数据项数。
int fread(void *ptr,int size,int nitems,FILE *stream);
int fwrite(void *ptr,int size,int nitems,FILE *stream);
fread()函数从流指针指定的文件中读取nitems个数据项,每个数据项的长度为size个字节,读取的nitems数据项存入由ptr指针指向的内存缓冲区中,
在执行fread()函数时,文件指针随着读取的字节数而向后移动,最后移动结束的位置等于实际读出的字节数。
该函数执行结束后,将返回实际读出的数据项数,这个数据项数不一定等于设置的nitems,因为若文件中没有足够的数据项,或读中间出错,都会导致返回的数据项数少于设置的nitems。
当返回数不等于nitems时,可以用feof()或ferror()函数进行检查。 fwrite()函数从ptr指向的缓冲区中取出长度为size字节的nitems个数据项,写入到流指针stream指向的文件中,执行该操作后,文件指针将向后移动,移动的字节数等于写入文件的字节数目。
该函数操作完成后,也将返回写入的数据项数。
五、非标准函数的读写
int open(char *filename, int access);
int read(int handle, void *buf, int count);
int write(int handle, void *buf, int count);
int lseek(int handle, long offset, int fromwhere);
一般用于终端设别、管道、网络非标准IO
int open(char *filename, int access);
int read(int handle, void *buf, int count);
int write(int handle, void *buf, int count);
int lseek(int handle, long offset, int fromwhere);
一般用于终端设别、管道、网络非标准IO
五、目标实现:
好了,有了上面的基础知识我们开始实现第一个小目标:
目标:1.把 A文件中的内容 读出并写入到 B 文件中,且在B文件的511字节和512字节赋值 0x55 0xaa
#include "stdio.h"#include <stdlib.h>void TestResult(FILE * fp){ if(feof(fp)) { //printf("Read OK\n"); //读取成功不许要打印消息 } else if(ferror(fp)) { printf("Read REEOR\n"); } }int main(){ char Shan[512] = {0}; FILE *fpA = NULL; FILE *fpB = NULL; int ret = 0; int DateNo = 512; fpA = fopen("A.txt","r");//源文件 fpB = fopen("B.txt","w+");//目的文件 if(0 != fflush(NULL)) { printf("flushall REEOR\n"); return 0; } if(NULL == fpA || NULL == fpB) { printf("open file error\n!"); return 0; } ret = fread(Shan, 1, DateNo,fpA);//读取A文件数据 if(ret != DateNo) { TestResult(fpA); } Shan[512-1-1] = 0x55;// 写512最后两字字节 Shan[512-1] = 0xaa; ret = fwrite(Shan, 1, DateNo,fpB);//把缓冲文件写入到B文件中 if(ret != DateNo) { TestResult(fpB); } if(0 != fcloseall())//关闭所有打开的文件 { printf("fcloseall REEOR\n"); return 0; } return 0;}
我们再来实现第二个小目标:
目标:2.指定A的内容:由汇编程序生成,写入到B中,且在B文件的511字节和512字节赋值 0x55 0xaa
其实第一个和第二个目标的区别在于读入的A文件不一样
我们结合之前的操作cpu寄存器的知识,写出A的汇编代码:
org 0x7c00jmp entryentry: mov ax, 0 mov ss, ax mov ds, ax mov es, ax mov si,msg putloop: ;按序执行 mov al, [si] add si, 1 cmp al, 0 je fin mov ah, 0x0e mov bx, 15 int 0x10 jmp putloop fin: HLT jmp finmsg: "Hello OS!" DB 0
使用汇编编译工具对A进行汇编:
nasm A.asm -o A.boot
这样生成的 A.boot 文件就是二进制文件,原来读写文件的c程序只要稍微改变一下打开A文件的地方就可以了,修改后程序如下:
#include "stdio.h"#include <stdlib.h>void TestResult(FILE * fp){ if(feof(fp)) { //printf("Read OK\n"); //读取成功不许要打印消息 } else if(ferror(fp)) { printf("Read REEOR\n"); } }int main(){ char Shan[512] = {0}; FILE *fpA = NULL; FILE *fpB = NULL; int ret = 0; int DateNo = 512; fpA = fopen("A.boot","rb");//1.修改文件名为A.boot 2.b表示打开对象为二进制文件 fpB = fopen("system.img","w+");//目的文件 if(0 != fflush(NULL)) { printf("flushall REEOR\n"); return 0; } if(NULL == fpA || NULL == fpB) { printf("open file error\n!"); return 0; } ret = fread(Shan, 1, DateNo,fpA);//读取A文件数据 if(ret != DateNo) { TestResult(fpA); } Shan[512-1-1] = 0x55;// 写512最后两字字节 Shan[512-1] = 0xaa; ret = fwrite(Shan, 1, DateNo,fpB);//把缓冲文件写入到B文件中 if(ret != DateNo) { TestResult(fpB); } if(0 != fcloseall())//关闭所有打开的文件 { printf("fcloseall REEOR\n"); return 0; } return 0;}
经过测试,生成的B文件中的确是A.boot的二进制文件,511和512字节分别为 0x55,0xaa
有了这一节的准备,下一节我们制作真正可以引导的操作系统映像文件将会容易很多。
阅读全文
0 0
- 自己动手写操作系统:2.C语言文件操作,制作系统引导程序
- 自己动手写操作系统-编写引导扇区
- 自己动手编写操作系统_引导程序
- 自己动手写操作系统 将引导程序成功写入优盘启动电脑
- C语言 写文件操作
- 操作系统内核Hack:(三)引导程序制作
- 大家一起写操作系统(1)-引导程序
- 自己动手写python的c语言扩展
- 自己动手写C语言编译器(1)
- 自己动手写C语言编译器(2)
- 自己动手写C语言编译器(3)
- 自己动手写C语言编译器(4)
- 自己动手写C语言编译器(5)
- 自己动手写C语言编译器(暂停)
- 自己动手写操作系统
- [转载]自己动手写操作系统
- 自己动手写操作系统(一)
- 自己动手写操作系统(二)
- 当代互联网网络坑很深,您中招了么?
- 递归
- cv1378 选课(树)
- ftrace学习
- Web学习日记17--------继续angularjs实现京东购物车
- 自己动手写操作系统:2.C语言文件操作,制作系统引导程序
- 单例模式的线程安全性问题浅析
- TENSORFLOW1.0运行之前版本代码报错解决
- 矩阵幂之和(矩阵乘法)
- 剑指offer:丑数
- TensorFlow不同版本引起的错误
- Android的5个进程等级
- Go语言学习笔记(一)程序结构
- 13-SpringBoot——Spring MVC基础-高级配置-文件上传