Linux 编程学习笔记----ANSI C 文件I/O管理 问题引入 问题解析

转载请注明出处:http://blog.csdn.net/suool/article/details/38129201


文件的种类

依据数据存储的方式不同,能够将文件分为文本文件和二进制文件.详细的差别和关系例如以下:

文本文件与二进制文件在计算机文件系统中的物理存储都是二进制的,也就是在物理存储方面没有差别都是01码,这个没有异议。他们的差别主要在逻辑存储上,也就是编码上。
文本文件格式存储时是将值作为字符然后存入其字符编码的二进制文本文件用‘字符’作为单位来表示和存储数据,比方对于1这个值,文本文件会将其看做字符‘1’然后保存其ASCII编码值(这里假定是ASCII编码),这样在物理上就是0x31这个二进制值。而若是二进制保存1,则直接保存其二进制值,比方假设程序中是处理1为整数则保存的二进制值就是 0x00000001 (4字节)。
当然假设程序本来就是按字符保存的 也就是 char ch ='1' ;则二进制保存后值就是其ASCII码。由于该变量的二进制本来就是其ASCII码。

能够总结出二进制文件就是值本身的编码,那么就是不定长的编码了,由于值本身就是不等字节的,如整数4个字节那么保存在二进制文件就是这四个字节的原生二进制值。



综上,能够知道文本文件与二进制文件就是编码方式不一样而已,而这个是用户行为,把一个数据以什么样的编码(字符还是值本身)存入文件是由用户主动选择的,也就是写入的接口选择。假设以二进制接口方式写入文件那么就是一个二进制文件,假设以字符方式写入文件就是一个文本文件了。

既然有写入时候的编码也就会有读出的编码,仅仅有两个编码相应才干读出正确的结果。如用记事本打开一个二进制文件会呈现乱码的。这里略微提一下后缀名,后缀名并不能确定其是否就是文本文件,二进制文件也能够是txt后缀名,后缀名仅仅是用来关联打开程序,给用户做备注用的。与文件的详细编码没有关系

能够使用字符接口读写二进制文件。仅仅须要做些处理就可以,所以所谓的二进制文件,文本文件主要体如今读写方式这里。此外windows有一个明显的差别是对待文本文件读写的时候,会将换行 自己主动替换成 。



最后文本文件和二进制文件主要是windows下的概念,UNIX/Linux并没有区分这两种文件。他们对全部文件一视同仁。将全部文件都看成二进制文件。

文件操作方式

依据应用程序对文件的訪问方式不同,能够分为带缓冲区的文件操作和非缓冲文件操作,详细差别简介例如以下:

缓冲文件操作:高级文件操作,将在用户空间中自己主动为正在使用的文件开辟内存缓冲区,遵循ANSI C标准的I/O函数就是缓冲文件操作

非缓冲文件操作:低级文件操作,假设须要,仅仅能由用户自己在程序中为每一个文件设置缓冲区,遵循POSIX标准的系统调用I/O函数就是这样的类型.

详细的差别见:http://www.360doc.com/content/11/0521/11/5455634_118306098.shtml


ANSI C库函数为了实现他的特性,採用了流的概念,在流的实现中,缓冲区是最重要的单元,依据须要能够分为全缓冲,行缓冲,无缓冲.

以下就详细讲一下关于ANSI  C的流和文件I/O相关的方法和函数.

问题解析

关于流及其功能

在linux系统中,系统默觉得每一个进程打开了三个文件,即是每一个进程能够默认操作三个流:标准输入流,输出流,错误流.

在系统源文件里的宏定义例如以下:

Linux 编程学习笔记----ANSI C 文件I/O管理
问题引入
问题解析

文件流的主要功能是:

1'格式化内容:实现不同输入输出格式转换.

2'缓冲功能:将数据读写集中,从而降低系统调用次数

文件流指针

在应用编程层面上,程序对流的操作主要是体如今文件流指针FILE上,操作一个文件之前,须要使用fopen打开文件,之后返回该文件的文件流指针与该文件关联,全部针对该文件的读写操作都是通过文件指针完毕.以下是在应用层可以訪问的FILE的结构体,因此结构体成员可以在用户空间訪问:

struct _IO_FILE {
  int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;   /* Current read pointer */
  char* _IO_read_end;   /* End of get area. */
  char* _IO_read_base;  /* Start of putback+get area. */
  char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr;  /* Current put pointer. */
  char* _IO_write_end;  /* End of put area. */
  char* _IO_buf_base;   /* Start of reserve area. */
  char* _IO_buf_end;    /* End of reserve area. */
..............
  int _fileno;                         / 文件描写叙述符
#if 0
}
上面那个结构体中包括了I/O库为管理该流所须要的全部信息.

以下是打印了一个打开的文件流指针指针成员信息的演示样例程序,在此程序中,使用标准的C库函数实现了文件的复制操作,在复制过程中,实时打印当前的读写位置和缓冲区信息.

代码例如以下:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define prt(CONTENT,MSG) printf(CONTENT":	%p
",MSG)

int main(int argc, char *argv[])
{
    FILE *fp_src,*fp_des;                                // 文件读写指针 
    char buffer[10],buffer1[128];                        // 文件读写缓冲区
    int i;
    if((fp_src=fopen(argv[1],"r+"))== NULL)	             // 从第一个命令行文件參数,读文件
    {
        perror("open1");
        exit(EXIT_FAILURE);
    }
    if((fp_des=fopen(argv[2],"w+"))== NULL)              // 从第二个命令行文件參数,写文件
    {
        perror("open2");
        exit(EXIT_FAILURE);
    }
    setvbuf(fp_src,buffer1,_IOLBF,128);                  // 显式设置缓冲区的位置和类型
    do
    {
        /*
         * 下面的prt内容均是属于提示性内容,是读取应用层所能訪问的FILE结构体得到的.
         */
        prt("src_IO_read_ptr",fp_src->_IO_read_ptr);     // 源文件读位置,source
        prt("_IO_read_end",fp_src->_IO_read_end);
        prt("_IO_read_base",fp_src->_IO_read_base);

        prt("src_IO_write_ptr",fp_src->_IO_write_ptr);
        prt("_IO_write_base",fp_src->_IO_write_base);
        prt("_IO_write_end",fp_src->_IO_write_end);

        prt("_IO_buf_base",fp_src->_IO_buf_base);        // 源文件缓冲区位置
        prt("_IO_buf_end",fp_src->_IO_buf_end);

        memset(buffer,'