IO笔记

1.C语言中文件的概念
1。所谓文件是指一组相关数据的有序集合,这个数据集有一个名称,叫文件名。如源程序文件,目标文件,可执行文件,头文件等。

2.文件通常是在驻留在外部介质上的,使用时 才调入内存。

3.从用户角度看,文件分为 普通文件设备文件两种。

普通文件
普通文件是指驻留在磁盘或其他外部介质上的一个有序数据集,可以是源文件,目标文件,可执行程序等。
可执行文件的大小问题,其具体有哪些内容决定其大小???
设备文件
设备文件是指与主机相关联的各种外部设备,如显示器,打印机,键盘等。
在操作系统中,把外设也看成一个文件来管理。它们的输入输出等同于对磁盘文件的读写。

4.文件的编码方式划分,文件可以分为文本文件和二进制文件。
文本文件
  • 以ACSII 码格式存放,一个字节存放一个字符。文本文件的每一个字节存放一个ASCII码,代表一个字符。这便于对字符的逐个处理,但占用存储空间较多,转换为二进制速度慢,但直观易记。

  • 比如:整数10000 0011000100110000001100000011000000110000 A 65 a 97
二进制文件
  • 二进制文件是把数据以二进制数的格式存放在文件中,但占用存储空间较少,无需转换。
  • 数据按其内存中的存储形式原样存放。一个字节不对应一个字符。故不能直接输出字符形式。
  • 比如 整数10000 0010011100010000

2.文件系统的分类
目前C语言所使用的磁盘文件系统分为缓冲文件系统和非缓冲文件系统
  • 缓冲文件系统
  1. 系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。从磁盘向内存读入数据时,则一次从磁盘文件将一些数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送给接收变量。向磁盘文件输出数据时,先将数据送到内存中的缓冲区,装满缓冲区后才一起送到磁盘区。
  2. 用缓冲区可以一次读入一批数据,或输出一批数据,而不是执行一次输入或输出函数就去访问一次磁盘,这样做的目的是减少对磁盘的实际读写次数,提高读写效率。

非缓冲文件系统
不是系统自动设置的缓冲区,而是用户自己根据需要设置的。

在传统的UNIX系统下,用缓冲文件系统处理文本文件,用非缓冲文件系统处理二进制文件
1983年ANSI C 标准决定不采用非缓冲文件系统,而只采用缓冲文件系统。即用缓冲文件系统处理文本文件,也用它来处理二进制文件。也就是将缓冲文件系统扩充为可以处理二进制文件
一般把缓冲文件系统的输入输出称为标准输入输出(标准IO),非缓冲文件系统的输入输出称为系统输入输出(系统IO)
在C语言中没有输入输出语句,对文件的读写都是库函数实现。ANSI C 规定了标准输入输出函数,用它们对文件进行读写。需要使用头文件  stdio.h

3.文件流的概念
C语言对文件的操作最终转化为对 缓存中的数据进行操作,流实际上就是内存中的一种具有 顺序操作的特殊数据,流如同流动的河水,有它的源和目的地。
流根据方向可以分为输入流和输出流
  • 输入流:从文件中读取数据的流
  • 输出流:将数据输出到文件的流
流根据数据内容可分为文本流和二进制流
  • 文本流:文本流是流动着的字符序列
  • 二进制流:流动着的二进制序列

三个标准流 (FILE*类型指针)
标准输入流stdin:针对标准输入键盘 scanf
标准输出流stdout:针对标准输出屏幕 printf
标准错误流stderr:针对标准输出屏幕
上述所有的流都统称为文件流
文件存储顺序,即文件流的数据结构是队列

4.文件类型结构体FILE和文件指针
文件类型结构体FILE
  • 缓冲文件系统为每个正使用的文件在内存开辟文件信息区。
  • 文件信息用系统定义的名为FILE的结构体描述。
  • FILE定义在stdio.h中
struct _iobuf {
char *_ptr; //文件输入的下一个位置 (位置指针)
int _cnt; //当前缓冲区的相对位置
char *_base; //指基础位置(即是文件的其始位置)
int _flag; //文件标志 判断是否可读可写
int _file; //文件描述符
int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
int _bufsiz; //缓存区尺寸
char *_tmpfname;//临时文件名

};
typedef struct _iobuf FILE;
stdin,stdout,stderr都是FILE *类型
文件指针的定义
FILE *指针变量名
  • 文件指针实际上是一个文件类型的结构体指针
  • 通过文件指针即可找到存放某个文件信息的文件类型结构体变量,然后按结构体变量提供的信息找到该文件,实施对文件的操作。
  • 文件打开时,系统自动创建文件类型结构体变量,并把指向它的指针返回来,程序通过这个指针获得文件信息并访问文件。
  • 文件关闭后,文件指针指向的结构体变量会被释放。


5.文件打开
FILE *fopen(char *filename, char *mode);
  • 功能:按指定方式打开文件
  • 参数
filename:为要打开的文件的路径.''
mode:使用文件的方式。
  • 返回:正常打开,返回文件指针;打开失败,返回NULL.
  • 标准输入,标准输出和标准错误由系统打开,可以直接使用。
示例
FILE *fp;
fp = fopen("test.txt","w");
使用文件的方式

符号定义
r(read) 读
w(write) 写
a(append) 追加
t(text)文本文件
b (binary) 二进制文件
+ 读和写
文件使用的处理方式


6.文件关闭
int fclose(FILE *fp);
  • 功能:关闭fp指向的文体,释放文件类型结构体和文件指针
  • 参数:fp打开文件时返回的文件指针。
  • 返回:成功返回0,失败返回-1.
  • 注意点:不关闭文件可能会丢失文件。
向文件写数据时,是先将数据输出到缓冲区,待缓冲区充满后才正式输出给文件,如果当数据未能充满缓冲区而程序结束运行,就会将缓冲区的数据丢失。
fclose 1.先把缓冲区数据输出到磁盘文件(刷新缓冲),2.然后才会释放文件类型结构体和文件指针。


7.ftell函数
int ftell(FILE* fp);
  • 功能:测试当前文件的读写位置
  • 返回:测试成功返回文件位置指针所在位置(返回字节数当前读写位置距离文件开头的字节数),失败返回-1;

实例:
FILE *fp;
fp=fopen("text.txt","w");
........
long pos = ftell(fp);

8.file_open_close.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    FILE *fp;
    fp = fopen("/etc/passwd","r");
    if(fp == NULL){
        printf("open file error\n");
        exit(EXIT_FAILURE);
    }

    int pos = ftell(fp);
    printf("pos :%d\n",pos);

    if(fclose(fp)!=0){
        printf("close file error\n");
        exit(EXIT_FAILURE);
    }
}

9.getchar和putchar函数
getchar函数
int getchar();
  • 功能:从标准输入stdin(键盘) 读取一个字符
  • 返回:成功返回读取的字符,失败返回EOF

putchar函数
int putchar(int ch);
  • 功能:将一字符ch写入标准输出stdout(屏幕)
  • 返回:成功返回写入的字符ch,失败返回EOF

fgetc
int fgetc(FILE *fp) fgetc(stdin) = getchar()
  • 功能:从fp指向的文件中读取一个字符
  • 返回:成功返回读取的字符,失败或到文件尾返回EOF(-1)
  • 可针对标准输入操作

fputc
int fputc(int ch,FILE *fp)
  • 功能:把一字符ch写入fp指向的文件中
  • 返回:成功返回写入的字符ch,失败返回EOF


10.getchar_putchar.c
#include <stdio.h>
int main(void)
{/*
    int ch;
    while((ch = getchar()) != '\n'){
        putchar(ch);
    }
    putchar('\n');
*/
     int ch;
     while((ch = getc(stdin))!= '\n'){
         putc(ch,stdout);
     }     
    /*
     ch = getchar();
     putchar(ch);
     ungetc(ch,stdin);
     putchar(ch);
     */
    return 0;
}

11.fgetc和fputc
copy.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *spIn,*spOut;
    spIn = fopen("/etc/passwd","r");
    if(spIn == NULL){
        printf("open file error\n");
        exit(EXIT_FAILURE);
    }

    spOut = fopen("passwd","w");
    if(spOut == NULL){
        printf("open file error\n");
        exit(EXIT_FAILURE);
    }
   
    int ch;
    while((ch = fgetc(spIn)) != EOF){
        fputc(ch,spOut);
    }

    if(fclose(spIn) != 0){
        printf("close file error\n");
        exit(EXIT_FAILURE);
    }
    if(fclose(spOut) != 0){
        printf("close file error\n");
        exit(EXIT_FAILURE);
    }

}

lines_chars.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
    if(argc != 2){
        printf("usage:%s filepath\n",argv[0]);
        exit(1);
    }
    
    int lines = 0;
    int chars = 0;

    FILE *spIn;
    spIn = fopen(argv[1],"r");
    if(spIn == NULL){
        printf("fopen file error\n");
        exit(1);
    }

    int ch;
    while((ch = fgetc(spIn)) != EOF){
        chars++;
        if(ch == '\n') lines++;
    }
    fclose(spIn);
    printf("lines total:%d\n",lines);
    printf("file size:%d bytes\n",chars);
    return 0;
}

12.统计文件中的单词数量
words.c
#include <stdio.h>
#include <stdlib.h>

#define WHITE_SPACE \
    (c == ' ' || c == '\t' || c == '\n')

int main(int argc, char *argv[])
{
    if(argc != 2){
        printf("usage:%s file\n",argv[0]);
        exit(1);
    }

    int c = 0;
    FILE *spIn = NULL;
    int words = 0;

    spIn = fopen(argv[1],"r");
    if(spIn == NULL){
        printf("open file error\n");
        exit(1);
    }

    while((c = fgetc(spIn)) != EOF) {
        if(WHITE_SPACE){
            words++;
        }
    }
    
    printf("total of word: %d\n",words);
    fclose(spIn);

    return 0;
}

13.二个文件进行合并,并且输出到第三个文件中(按字母顺序排列)
merger.c
#include <stdio.h>
#include <stdlib.h>

void sort(char content[], int n);

int main(int argc,int *argv[])
{
    if(argc != 4) {
        printf("usage:%s file_A file_B file_C\n",argv[0]);
        exit(1);
    }

    FILE *fp1;
    fp1 = fopen(argv[1],"r");
    if(fp1 == NULL){
        printf("file_A open error\n");
        exit(EXIT_FAILURE);
    }

    FILE *fp2;
    fp2 = fopen(argv[2],"r");
    if(fp1 == NULL){
        printf("file_B open error\n");
        exit(EXIT_FAILURE);
    }
    
    FILE *fp3;
    fp3 = fopen(argv[3],"w");
    if(fp1 == NULL){
        printf("file_C open error\n");
        exit(EXIT_FAILURE);
    }
printf("hello 1\n");    
    char content[100] = {'\0'};
    int i = 0;
    int ch;
    while((ch = fgetc(fp1))!= EOF) {
        putchar(ch);
        if(ch == '\n' || ch == ' '|| ch == '\t'){
            continue;
        }
        content[i++] = ch;
    }
    fclose(fp1);   

printf("hello 1\n");    
    while((ch = fgetc(fp2))!= EOF) {
        putchar(ch);
        if(ch == '\n' || ch == ' '|| ch == '\t'){
            continue;
        }
        content[i++] = ch;
    }
    fclose(fp2);   

printf("hello 1\n");    
   int n = i; 
   sort(content,n);

   for (i = 0; i< n; i++){
       putchar(content[i]);
       fputc(content[i],fp3);



   }

   putchar('\n');
   fputc('\n',fp3);
   fclose(fp3);
    return 0;

}

void sort(char content[],int n)
{
    int i,j;
    for(i = 0; i < n -1; i++){
        int pos = i;
        for(j = i+ 1; j<n; j++){
            if(content[j] < content[pos]){
                pos = j;
            }
        }
        if(pos != i){
            char ch;
            ch = content[pos];
            content[pos] = content[i];
            content[i] = ch;
        }
    }
}
14.fgets和fputs函数
fgets函数 '\n' (只能用于文本文本,不能用于二进制文件)
char *fgets(char *str, int size, FILE *fp);
  • 功能:从fp所指向文件中至多读size-1个字符,放入str指向的字符数组中,如果在读入size-1个字符结束前遇到换行符或者EOF,读入即结束,字符串读入后在最后加一个‘\0'字符。
  • 返回:成功返回str指针,失败或者文件结束返回NULL
  • 可针对标准输入操作
1.如果读取的一行内容小于size ,一次性读出放入str中
2.如果读取的一行长度大于size,分几次读取。

fputs函数 (只能用于文本文本,不能用于二进制文件)
int fputs(char *str,FILE *fp);
  • 功能:把str指向的字符串或者字符数组写入fp指向的文件中
  • 返回:成功返回0,出错返回EOF.
  • 可针对标准输出操作
fgets与fputs暂时的缓存数组在fgets后自动在数组内加入\0,这样一来就可以把一行全部变成字符了,包括\n。在用fputs的时候会自动省略\0。这样就能原封不动的把fgets来的数据fputs出去了。 但是,\n只在文本文件中存在,所以fgets与fputs不能用于二进制文件

15.copy2.c
利用fgets和fputs完成文件的复制
#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
    if(argc != 3){
        printf("usage:%s source target\n",argv[0]);
        exit(1);
    }

    FILE *spIn,*spOut;
    spIn = fopen(argv[1],"r");
    if(spIn == NULL){
        printf("source file open error\n");
        exit(EXIT_FAILURE);
    }

    spOut = fopen(argv[2],"w");
    if(spOut == NULL){
        printf("target file open error\n");
        exit(EXIT_FAILURE);
    }

    char content[100];
    while(fgets(content,sizeof(content),spIn)!=NULL){
        fputs(content,spOut);
    }
    fclose(spIn);
    fclose(spOut);
    return 0;
}
16.格式化读写函数 fscanf和fprintf
int fscanf( FILE *fp,const char* format,...);
int fprintf (FILE *fp,const char *format,...);
  • 功能:按format格式对fp指向的文件进行IO操作
  • 返回:成功返回IO的字节数,失败或到文件尾返回EOF
  • 可针对标准输入和标准输出操作
  • 示例
fscanf(fp,"%d%f",&i,&t); scanf(“%d%f",&i,&t); = fscanf(stdin,"%d%f",&i,&t);
fprintf(fp,"%d,%f",i,t); printf("%d,%f",i,t); = fprintf(stdout,"%d,%f",i,t);

17. student_score.c
统计学生成绩并计算等级输出到另外一个文件中
读取score.txt文件
#include <stdio.h>
#include <stdlib.h>

char get_grade(float average);

int main(int argc, char *argv[])
{
    if(argc != 3){
        printf("usage: %s source target\n",argv[0]);
        exit(1);
    }

    int xh;
    char name[20] = {'\0'};

    int score1,score2,score3;
    char grade;
    float average;

    FILE *spIn;
    spIn = fopen(argv[1],"r");

    if(spIn == NULL){
         printf("open source file error\n");
         exit(EXIT_FAILURE);
    }
   
    FILE *spOut;
    spOut = fopen(argv[2],"w");
    if(spOut == NULL){
        printf("open target file error\n");
        exit(EXIT_FAILURE);
    }

    while(fscanf(spIn,"%d %s %d %d %d",&xh,name,&score1,&score2,&score3)!=EOF){
        average = (score1 + score2 + score3)/3.0;
        grade = get_grade(average);
        fprintf(spOut,"%d %s %.2f %c\n",xh,name,average,grade);
    }
    
    return 0;
}
           
char get_grade(float average)
{
    if(average >= 90)
        return 'A';
    else if(average >= 80)
        return 'B';
    else if(average >= 70)
        return 'C';
    else 
        return 'D';
}

计算等级
>=90 A
>=80 B
>=70 C
输出到

18.sscanf和sprintf函数
int sscanf(char const *str,const char *format,...);
int sprintf(char const *str,const char *format,...);
功能:按format格式对str指向的字符数组进行IO操作
返回:成功返回IO字节数,失败返回EOF
示例:
char *str = "100 farsight";
int id; char name[50];
sscanf(str,"%d %s",&id ,name);
char stu[100];
sprintf(stu,"%d %s",id,name);

sscanf与sprintf是对于字符串的操作,而不是文件,对象不同的!只是操作方式类似,当然类似
(字符串,“格式”,变量)

19.sscanf和sprintf的使用
sscanf_sprintf.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char *str = "hello farsight 2018";
    char str1[10] = {'\0'};
    char str2[10] = {'\0'};
    int year = 0;
    char array[50] = {'\0'};

    sscanf(str ,"%s %s %d",str1,str2,&year);
    sprintf(array,"%s %s %d",str1,str2,year);
    printf("arry:%s\n",array);

    char nums[20] = {'\0'};
    int number = 123456;
    sprintf(nums,"%d",number);
    printf("%s\n",nums);

    return 0;
}
20.fread和fwrite 函数(二进制文件读写)
fread和fwrite函数
int fread(void *buffer,int num_bytes,int count, FILE *fp);
int fwrite(void *buffer,int num_bytes,int count, FILE *fp);
  • 功能:读写数据块,一般用于二进制文件的输入输出。
  • 返回:成功返回读写的元素个数,失败或到文件尾返回EOF
  • 参数:
buffer:一个指向要进行输入输出数据存储区的通用指针
num_bytes: 每个要读写的元素的字节数
count:要读写的元素个数
fp:要读写的文件指针

stu是student结构体变量
私以为这两个会了,其它也就都会了
1.文本文件转换为二进制文件

2.二进制文件转换为文本文件

21. read_write_student.c
读取student.txt文件存储到结构体变量stu中,然后把stu写到二进制文件中,
然后再从二进制文件读出来写到另外一个文本文件中。
1 zhangsan 20
2 lisi 22
3 wangwu 21
文件拷贝代码,位置指针是会变的
#include <stdio.h>
#include <stdlib.h>

void text_to_bin();
void bin_to_text();

typedef struct
{
    int xh;
    char name[20];
    int age;
}Student;

int main(int argc, char *argv[])
{
    if(argc != 4){
        printf("usage:%s source target1 target2\n",
            argv[0]);
        exit(1);
    }
    text_to_bin(argv);
    bin_to_text(argv);
    return 0;

}

void text_to_bin(char *argv[])
{
    Student stu;
    FILE *spIn, *spOut;

    spIn = fopen(argv[1],"r");
    if(spIn == NULL){
        printf("open source file error\n");
        exit(EXIT_FAILURE);
    }

    spOut = fopen(argv[2],"wb");
    if(spOut == NULL){
        printf("open target1 file error\n");
        exit(EXIT_FAILURE);
    }
    
    while(fscanf(spIn,"%d %s %d",&stu.xh,stu.name,&stu.age)!=EOF){     //text_to_bin
        fwrite(&stu,sizeof(stu),1,spOut);
    }
    fclose(spIn);
    fclose(spOut);

}

void bin_to_text(char *argv[])
{
    Student stu;
    FILE *spIn,*spOut;
    spIn = fopen(argv[2],"rb");
    if(spIn == NULL)
    {
        printf("open target1 file error\n");
        exit(EXIT_FAILURE);
    }
    spOut = fopen(argv[3],"w");
    if(spOut == NULL){
        printf("open target2 file error\n");
        exit(EXIT_FAILURE);
    }
#if 0
    while(fread(&stu,sizeof(stu),1,spIn)){                                                  //bin_to_text
        fprintf(spOut,"%d %s %d\n",stu.xh,stu.name,stu.age);
    }
#else
    size_t size = fread(&stu,sizeof(stu),1,spIn);
    if(size == 0) return;
    while(!feof(spIn)){
        fprintf(spOut,"%d %s %d\n",stu.xh,stu.name,stu.age);
        fread(&stu,sizeof(stu),1,spIn);
    }
#endif
    fclose(spIn);
    fclose(spOut);
}
26.其他函数
void rewind(FILE *fp);
功能:使文件位置指针重新返回文件首

int remove(const char *filename);
功能:用于删除指定文件
返回:成功删除返回0,失败返回-1

void fflush(FILE *fp);
功能:刷新缓冲区,如果打开文件进行读操作,该函数将清空文件的输入缓冲区,如果打开文件进行写操作,该函数将文件的输出缓冲区内容写入文件中。

27.标准IO总结


struct _iobuf {
char *_ptr; //文件输入的下一个位置
int _cnt; //当前缓冲区的相对位置
char *_base; //指基础位置(即是文件的其始位置)
int _flag; //文件标志
int _file; //文件描述符
int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
int _bufsiz; //缓存区尺寸
char *_tmpfname;//临时文件名

};
typedef struct _iobuf FILE;
stdin,stdout,stderr都是FILE *类型

28.标准I/O提供了三种类型的缓存
刷新缓存区
  1. fflush
  2. 程序结束
  3. \n
  4. fclose
全缓存 (针对文件)
要求填满整个缓存区后才进行IO系统调用操作,对于磁盘文件通常使用全缓存访问。
1。当缓冲区满了,或者满足一定的条件后,就会执行刷新操作。
2。刷新(fflush):标准I/O的写操作。
3。程序结束自动清缓存

行缓存 (针对标准输入输出终端)
1。当在输入和输出中遇到新换行符(‘\n’)时,进行I/O操作。
2。行缓冲区满了自动输出
3。涉及一个终端时(例如标准输入和标准输出),使用行缓存。
#include<stdio.h>
int main(void)
{
	printf("hello iotek");
	while(1){
		sleep(1);
	}
	return 0;
}

不带缓存
1。标准I/O库不对字符进行缓冲,例如stderr。使得错误信息尽快输出。
2。很多的人机交互界面要求不可全缓存。
3。标准出错决不会是全缓存的。
stderr不带缓存

3,缓存有什么好处呢?

可以把程序向输出流写数据比做从北京运送烤鸭到上海。如果没有缓冲区,那么每执行一次write(int b)方法,仅仅把一只烤鸭从北京运到上海,如果由一万只烤鸭,就要运送一万次,这样的运送效率显然很低。为了减少运送次数,可以先把一批烤鸭装到一个集装箱中,这样就能成批的运送烤鸭,这个集装箱就是缓冲区。
在默认的情况下,只有当这个集装箱装满后,才会把这箱烤鸭运到上海(全缓存),而flush方法表示不管集装箱是否装满,都执行一次运货操作。

29.文件IO系统调用
  • open() 打开文件
  • create() 创建文件
  • close() 关闭文件
  • read() 读取文件
  • write() 写入文件
  • lseek() 文件定位
这些不带缓存的函数都是内核提供的系统调用。它们不是ANSI C的组成部分,但是是POSIX的组成部分。
文件io在内核里面,系统调用。 这个在以后还会讲
标准io是对文件io的封装并加了缓存。

标准库函数
遵守ISO标准,基于 的IO,对 文件指针(FILE结构体)进行操作。
系统调用
兼容POSIX标准,基于 文件描述符的IO,对 文件描述符进行操作。

30.文件描述符
关于文件描述符
对于内核而言,所有打开的文件都有文件描述符应用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open或create返回的文件描述符标识该文件,将其作为参数传送给read或write。

在POSIX应用程序中,整数0,1,2被替换成符号常数STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO。这些常数都定义在头文件<unistd.h>中。

文件描述符的范围是0-OPEN_MAX。早期的UNIX版本采用的上限是19,允许每个进程打开20个文件。现在很多系统将其增加得到1024.

表就是一个数组,里面有很多指针,指针指向文件表项,上图的一行代表一个文件表项
open函数返回的就是数组的下标,一个进程一个表,

进程与内存的映射, 这些表存在内核当中。由各个进程共享。Linux内核系统调配来管理,只有进程结束才会失效???不对吧??
31.open函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname,int flags); 打开文件
int open(const char *pathname,int flags,mode_t mode); 新建文件
参数
  • pathname:要打开或者要创建的文件路径
  • flags:用来说明此函数的多个选择项
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
  • mode: 新建文件的访问权限,对于open函数而言,仅当创建新文件时才使用第三参数。


open函数的flags参数
O_RDONLY(只读),
O_WRONLY(只写),
O_RDWR(可读可写)

O_APPEND 每次写操作都写入文件的末尾
O_CREAT 如果指定文件不存在,则创建这个文件,按mode参数指定文件的权限
O_EXCL 如果要创建的文件已存在并且O_CREAT也存在,则返回 -1,并且修改errno的值
O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O
设置为非阻塞模式(nonblocking mode)

/usr/inclulde/stdio.h
比如:open(pathname,O_WRONLY|O_CREAT|OTRUNC,mode);
O_CREAT|O_EXCL
返回文件描述符,错误返回负数

32.creat函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat(const char *pathname,mode_t mode);
返回:若成功为只写打开的文件描述符,若出错为-1
功能:创建一个新文件
这个函数等同于open(pathname,O_WRONLY|O_CREAT|OTRUNC,mode);
creat的一个不足之处是它以只写的方式打开所创建的文件

33.close函数
#include <unistd.h>
int close(int fd);
返回:若成功为0,失败为-1
功能:关闭一个打开的文件

参数:fd 已打开的文件描述符
当一个进程终止时,它所有打开的文件都有内核自动关闭。

34.read函数
#include <unistd.h>
ssize_t read(int fd,void *buf, size_t count);
  • 返回:读到的字节数,若已文件尾为0,若出错为-1
  • 功能:从打开的文件中读数据

参数
  • fd : 读取文件的文件描述符
  • buf:存放读取数据的缓存
  • count:要求读取一次数据的字节数
有多种情况可以使实际读到的字节数少于要求读字节数。
  • 读普通文件时,在读到要求的字节数之前已到达了文件尾端
  • 当从终端设备读时,通常一次最多读一行
  • 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数
  • 进程由于信号造成中断
读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数。

35.write函数
#include <unistd.h>
ssize_t write(int fd,const void *buf, size_t count);
返回:若成功为已写的字节数,若出错为-1
功能:向打开的文件中写数据

参数
fd:写入文件的文件描述符
buf: 存放待写数据的缓存
count:要求写入一次数据的字节数

其返回值通常与参数count的值相同,否则表示出错
write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制
对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。
36文件读写案例
include/io.h
#ifndef _IO_H_
#define _IO_H_

extern void copy(int fdin, int fdout);

#endif
src/copy.c
#include "io.h"
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
    if(argc != 3){
        fprintf(stderr,"usage: %s srcfile destfile\n",argv[0]);
        exit(1);
    }
    
    int fdin,fdout;

    fdin = open(argv[1],O_RDONLY);
    printf("file length: %ld\n",lseek(fdin,0,SEEK_END));
    if(fdin < 0){
        fprintf(stderr,"open error: %s\n",strerror(errno));
        exit(1);
    }

    fdout = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0777);
    if(fdout < 0){
        fprintf(stderr,"creat error:%s\n",strerror(errno));
        exit(1);
    }
    copy(fdin,fdout);
    close(fdin);
    close(fdout);

    return 0;
}
src/io.c
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#include "io.h"
#define BUFFER_LEN 1024

void copy(int fd1,int fd2)
{
    char buffer[BUFFER_LEN];
    ssize_t size;

    while((size = read(fd1,buffer,BUFFER_LEN))> 0){
            if(write(fd2,buffer,size)!= size){
                fprintf(stderr,"write error :%s\n",strerror(errno));
                exit(1);
            }
    }
    if(size < 0){
        fprintf(stderr,"read error : %s\n",strerror(errno));
        exit(1);
    }

}
此案例接着就是静态库,动态库,下面

37.lseek函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset,int whence);
  • 返回:成功则返回新的文件位移量(绝对位移量(相对开头)),若出错返回-1
  • 功能:定位一个已打开的文件

参数:
  • fd:已打开文件的文件描述符
  • offset:位移量 按字节偏移
  • whence:定位的位置
SEEK_SET:将该文件的位移量设置为文件开始处offset个字节
SEEK_CUR:将该文件的位移量设置为当前值加offset,offset可为正或负
SEEK_END:将文件的位移量设置为文件长度加offset,offset可正可负。

lseek也可以用来确定所涉及的文件是否可以设置位移量。如果文件描述符引用的是一个管道或FIFIO,则lseek返回-1.
每一个打开文件都有一个与其相关联的“当前文件偏移量”。它是一个非负整数,用以度量从文件开始处计算的字节数。通常,读写操作从文件的偏移量处开始,并使偏移量增加所读写的字节数,按系统默认,当打开一个文件时,除非指定O_APPEND选择项,否则该位移量被设置为0.

lseek按字节偏移

38.lseek.c
#include "io.h"
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
    if(argc != 2){
        fprintf(stderr,"usage: %s srcfile destfile\n",argv[0]);
        exit(1);
    }
    
    int fdin;

    fdin = open(argv[1],O_RDONLY);
    if(fdin < 0){
        fprintf(stderr,"open error: %s\n",strerror(errno));
        exit(1);
    }
	
   	printf("file length: %ld\n",lseek(fdin,0,SEEK_END));  获得文件长度
printf("file current position: %d\n",lseek(fdin,0,SEEK_CUR));  获取文件当前位置

    return 0;
}

39.空洞文件
位置指针超过文件所有,产生空洞文件
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

char *buffer = "0123456789";

int main(int argc, char* argv[])
{
    if(argc < 2){
        fprintf(stderr,"-usage %s [file]\n",argv[0]);
        exit(1);
    }

    int fd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0777);
    if(fd < 0){
        perror("open error");
        exit(1);
    }

    size_t  size = strlen(buffer)*sizeof(char);

    if(write(fd,buffer,size) != size){
        perror("write error");
        exit(1);
    }

    if(lseek(fd,10,SEEK_END)<0){
        perror("lseek error");
        exit(1);
    }

    if(write(fd,buffer,size)!= size){
        perror("write error");
        exit(1);
    }

    close(fd);
    return 0;
}
hole_file.c
more
od -c hole.txt
df -k
sudo tune2fs -l /dev/sda1 查看磁盘块大小

1234 1234
空洞文件,一般cat文件会是1234
若vim 此文件,显示 1234 乱码 1234
40.静态库
链接时就把文件写入目标文件

gcc -c io.c 生成io.o
ar crs libmyio io.o libmyio就是静态库了

gcc -o mycp -Iinclude src/copy.c -L./src -lmyio
gcc -o 生成文件 -I头文件位置 所需源代码 -L位置 静态库文件名
41.动态库
执行时再包含库,链接时只是把库路径包含
gcc -fPIC -Wall -c io.c 生成位置无关的代码 在进程阶段再讲
gcc -shared -o libmyio.so io.o 生成动态库

gcc -o mycp -Iinclude src/copy.c -L./src -lmyio
gcc -o 生成文件 -I头文件位置 所需源代码 -L位置 -l文件名
可以 cp 动态库文件 lib目录内 , 静态库不用这样的

export LD_LIBRARY_PATH="./src" 增加链接库的路径(改变环境变量)



42.一个打开的文件在内核中使用三种数据结构表示

文件描述符表
  • 文件描述符标志
  • 文件表项指针

文件表项
  • 文件状态标志
  • 读写追加,同步和非阻塞等状态标志
  • 当前文件偏移量。
  • i节点表项指针
  • 引用计数器
i节点
  • 文件类型和该文件的操作函数指针
  • 当前文件长度
  • 文件所以者
  • 文件所在设备,文件访问权限
  • 指向文件数据在磁盘块上所在位置的指针等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值