当前位置:   article > 正文

文件操作open与fopen和read与fread的区别

fopen和read

转自:http://blog.csdn.net/dreamintheworld/article/details/52336052

系统调用的文件操作分别直接基于IO无缓存操作以及带有缓存操作; 
不带缓存的函数特点是直接对文件(包括设备)进行读写操作; 
不带缓存的函数不是ANSI C的组成部分,是POSIX的组成部分 
都是基于文件描述符,基于基本的IO控制,不带缓存;

关于不带缓存的情景: 
运行系统调用时,Linux必须从用户态切换到内核态,执行相应的请求,然后在返回到用户态; 
如果不带缓存会频繁的运行系统调用,会降低程序的效率; 
标准I/O操作提供流缓存,目的就是尽可能减少使用read()和wirte()等不带缓存的系统调用; 
比如:printf(), scanf();

不带缓存直接进行IO操作 基于缓存对文件进行操作
int open(const char*path, int flags, int perms) FILE *fopen(const char* path, const char* mode)
FILE *fdopen(int fd, const char* mode)
FILE *freopen(const char*path, const char*mode, FILE*stream)
int close(int fd) int fclose(FILE *stream)
ssize_t read(int fd, void *buf, size_t count) size_t fread(void*ptr,size_t size,size_t nmemb,FILE*stream)
ssize_t write(int fd, void *buf, size_t count) size_t fwrite(const void*ptr,size_t size,size_t nmemb,FILE*stream)
off_t lseek(int fd, off_t offset, int whence) int fseek(FILE *stream, long offset, int fromwhere);


罗列了他们之前的声明,那么该如何选择呢: 
使用带缓存操作的特点: 
1.存在缓冲,操作外存次数减少,执行速度快效率高 
2.fopen是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api 
3.fopen是标准c函数。返回文件流而不是linux下文件句柄 
4.fopen可移植,open不能 
使用直接操作IO: 
1.open为系统调用,直接返回文件句柄 
2.因为UNIX系统中设备驱动都是文件形式所以推荐使用open进行IO操作 
3.设备文件不可以当成流式文件来用,只能用open

标准IO提供了3种类型的带缓冲存储; 
1 全缓冲 
2 行缓冲 
3 无缓冲 
自行查找资料了解相关知识点;

一般用fopen打开普通文件,用open打开设备文件

下面分别使用两种方式读取和写入文件: 
读取文本中的文件并写入新的文件中(dest_file) 
例程来自《嵌入式Linux应用程序开发标准教程》;

  1. //copy_file.c
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #define BUFFER_SIZE 1024// buffer size every read
  9. #define SRC_FILE_NAME "copy_file.c"//"src_file" // source file
  10. #define DEST_FILE_NAME "dest_file" // copy to this file
  11. #define OFFSET 10240 // the lastest data
  12. int main()
  13. {
  14. int src_file, dest_file;
  15. unsigned char buff[BUFFER_SIZE];
  16. int real_read_len;
  17. src_file = open(SRC_FILE_NAME, O_RDONLY);
  18. dest_file = open(DEST_FILE_NAME, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
  19. if (src_file < 0 || dest_file < 0) {
  20. printf("Open file error\n");
  21. exit(1);
  22. }
  23. lseek(src_file, -OFFSET, SEEK_END);
  24. while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0) {
  25. write(dest_file, buff, real_read_len);
  26. }
  27. close(dest_file);
  28. close(src_file);
  29. return 0;
  30. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

下面使用带有缓存的fopen进行操作上面的函数: 
因为读取非驱动的普通文本文件,所以open和fopen可以互相替代 
只是需要修改相关函数参数以及变量即可: 
下面只列出修改部分(即main函数)

  1. int main()
  2. {
  3. FILE *src_file, *dest_file;
  4. unsigned char buff[BUFFER_SIZE];
  5. int real_read_len;
  6. src_file = fopen(SRC_FILE_NAME, "r");
  7. dest_file = fopen(DEST_FILE_NAME, "w+");
  8. if (src_file < 0 || dest_file < 0){
  9. printf("Open file error\n");
  10. exit(1);
  11. }
  12. fseek(src_file, -OFFSET, SEEK_END);
  13. while ((real_read_len = fread(buff,1, BUFFER_SIZE, src_file)) > 0) {
  14. fwrite(buff, sizeof(char), real_read_len, dest_file);
  15. }
  16. fclose(dest_file);
  17. fclose(src_file);
  18. return 0;
  19. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

makefile文件:

  1. EXEC = copy_file
  2. OBJS = copy_file.o
  3. HEADERS =
  4. CC = gcc
  5. INC = -I.
  6. CFLAGS = ${INC} -g
  7. all:${EXEC}
  8. ${EXEC} : ${OBJS}
  9. ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJS}
  10. ${OBJS} : ${HEADERS}
  11. .PHONY : clean
  12. clean :
  13. -rm -f ${OBJS} ${EXEC}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

文件打开后的其他细节方面的操作为: 
主要分为3种操作:

字符输入输出 行输入输出 格式化输入输出
getc/putc
fgetc/fputc
getchar/putchar
gets/fgets
puts/fputs
printf/fprintf/sprintf
vprintf/vfprintf/vsprintf
scanf/fscanf/sscanf
int getc(FILE*stream)
int putc(int c,FILE*stream)
int fgetc(FILE*stream)
int fputc(int c,FILE*stream)
int putchar(int ch);
int getchar(void);
char*gets(char*str)
int puts(const char*str)
int fputs(const char*s, FILE*stream)
char*fgets(char*s,int size,FILE*stream)
int printf(const char*format, …)
int fprintf(FILE*fp,const char*format, …)
int sprintf(char*buf,const char*format, …)
int scanf(const char*format, …)
int fscanf(FILE*fp,const char*format, …)
int sscanf(char*buf,const char*format, …)


字符输入输出测试,分别测试控制台以及本地文件: 
1.控制台读写测试:

  1. // test.c
  2. #include <stdio.h>
  3. main(){
  4. int c;
  5. fputc(fgetc(stdin), stdout);
  6. //fputc(fgetc(stdin), stdout);
  7. //fputc(fgetc(stdin), stdout);
  8. fputc('\n',stdout);
  9. //putc(getc(stdin), stdout);
  10. //putc(getc(stdin), stdout);
  11. //putc(getc(stdin), stdout);
  12. //putc('\n',stdout);
  13. //putchar(getchar());
  14. //putchar(getchar());
  15. //putchar(getchar());
  16. //putchar('\n');
  17. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

注释掉的测试代码为: 
如果输入字符串在缓存中等待,使用getchar()将会依次读取字符串中的字符出来; 
2.本地文件读写测试:

  1. //testfile.c
  2. #include<stdio.h>
  3. main(){
  4. FILE *filedes;
  5. char* DEST_FILE_NAME = "destfile";
  6. filedes = fopen(DEST_FILE_NAME, "w+");
  7. int res = fputs("Hello World\n", filedes);
  8. fclose(filedes);
  9. filedes = fopen(DEST_FILE_NAME, "r");
  10. if(filedes>0){
  11. puts("------");
  12. int result;
  13. while(EOF != (result=fgetc(filedes))){
  14. putc(result, stdout);
  15. }
  16. fputs("------\n", stdout);
  17. }
  18. }
  19. //运行结果:
  20. root@ubuntu:/home/demo/LinuxAPP# gcc testfile.c
  21. root@ubuntu:/home/demo/LinuxAPP# ./a.out
  22. ------
  23. Hello World
  24. ------
  25. root@ubuntu:/home/demo/LinuxAPP#
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

getc/fgetc/putc/fputc 都是指定流中读写操作 
getchar/putchar 是在固定的流(控制台)中读写操作 
所以控制台读写本质上读写都是文件;

我们常见的终端输入输出就是一种简单的字符操作:

  1. /*
  2. 在Unix系统下,读取键盘输入,向屏幕输入信息.
  3. 手动打开键盘和显示器文件并进行读写.
  4. */
  5. #include <stdio.h>
  6. int main(void)
  7. {
  8. FILE *pf;
  9. pf=fopen("/dev/tty","a+"); //终端字符文件
  10. char sbuf[100];
  11. fprintf(pf, "Please input a string: ");
  12. fscanf(pf,"%s",sbuf);
  13. fprintf(pf,"The input string is : \"%s\".\n",sbuf);
  14. return 0;
  15. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

注:/dev/tty为终端字符文件,该文件是对键盘,显示器的抽象, 
向该文件写入,则写入内容将被显示在显示器; 
读该文件,则将从键盘获得输入;

所以可以看出,其实标准的输入输出是基于流的形式; 
也是节约多次调用内核空间的方式;

因为标准输入输出,是非常常用的操作,因此ANSI C标准中; 
把printf和scanf用于从标准输入获取信息和向标准输出显示信息的函数; 
于是根据标准,输入输出可以简化为:

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. char sbuf[100];
  5. printf("Please input a string: ");
  6. scanf("%s",sbuf);
  7. printf("The input string is : \"%s\".\n",sbuf);
  8. return 0;
  9. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9


int sprintf( char *buffer, const char *format, … ); 
把格式化的数据写入某个字符串中

int vsprintf(char *string, char *format, va_list param); 
将param 按格式format写入字符串string中 
注: 该函数会出现内存溢出情况,建议使用vsnprintf

scanf/fscanf/sscanf 函数基于基本同上; 
格式化常用为printf/scanf ; 
没必要例程测试;

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/256458?site
推荐阅读
相关标签
  

闽ICP备14008679号