Linux应用编程(高级编程)

1. 文件编程

- 文件编程通过库函数或系统调用来实现。

1.标准IO——库函数的方式。

2.文件IO——Linux操作系统提供——系统调用。
- 文件类型包括文本文件和二进制文件,如mp3、jpeg等。

2. Linux文件系统

- Linux内核将一切视为文件,包括硬件设备。

思想:一切皆文件:everything is file!
- 文件类型有块设备、字符设备、目录、普通文件、软连接、管道和套接字。

包括七种:b c d - l p s 

b -- block      -- 块设备文件   --- 硬盘 ---- ls -l /dev/sda 
c -- character  -- 字符设备文件 --- 鼠标 ---- ls -l /dev/input/
d -- directory  -- 目录文件     
-  -- regular    -- 普通文件 
l  -- link       -- 软连接文件   --- 类似windows的快捷方式  
p -- pipe       -- 管道文件     --- 实现操作系统中 进程间的一些 信息交换(通信)
s -- socket     -- 套接字文件   --- 网络的时候 (进程间的通信)

3. 文件操作基本步骤

- 打开文件:使用系统调用或库函数。
- 读写操作:使用标准IO库函数。
- 关闭文件:释放资源。

标准IO库:
根据不同的操作系统了不同的函数接口来帮助我们实现想要的功能!
1.打开 -- fopen  //FILE open
2.读写 -- getchar/putchar 
          gets / puts 
          fread / fwrite 
3.关闭 -- fclose 

4. 文件打开模式

- 只读(`r`)、读写(`r+`)、只写(`w`)、追加(`a`)等模式。

#include <stdio.h>
FILE* fopen(const char *path, const char *mode);
功能:
     流打开函数 (打开文件,并关联到一个流)
参数:
    @path --要打开的文件的文件名(字符串形式)
    @mode --打开文件的操作模式
        r ---打开文件做读操作
             注意:
             文件必须存在 
             
        r+   打开文件做读写操作
             注意:
             文件必须存在
        w    打开文件做写操作
             注意:
             如果文件已经存在,则会将文件截断为0
             如果文件不 存在,则会创建一个新文件。
             
        w+  打开文件做读写操作
             注意:
             如果文件已经存在,则会将文件截断为0
             如果文件不 存在,则会创建一个新文件。

        a    打开文件做写操作
             注意:
             如果文件已经存在,则在文件末尾进行写入 
             如果文件不存在,则会创建一个新文件。
        a+   
            打开文件做读写操作
             注意:
             如果文件已经存在,则在文件末尾进行写入 
             如果文件不存在,则会创建一个新文件。
        
        
       r      Open  text  file  for reading.  The stream is positioned at the beginning of the
              file.

       r+     Open for reading and writing.  The stream is positioned at the beginning of  the
              file.

       w      Truncate  file  to  zero  length or create text file for writing.  The stream is
              positioned at the beginning of the file.

       w+     Open for reading and writing.  The file is created if it does not exist,  other‐
              wise it is truncated.  The stream is positioned at the beginning of the file.

       a      Open for appending (writing at end of file).  The file is created if it does not
              exist.  The stream is positioned at the end of the file.

       a+     Open for reading and appending (writing at end of file).  The file is created if
              it does not exist.  The initial file position for reading is at the beginning of
              the file, but output is always appended to the end of the file.
        
返回值:
   成功 FILE * 
   失败 NULL 并且 设置 errno 表明错误原因 

5.FILE * 流指针:
  
     

FILE * fp 
     (1).流
      FILE * fp;//流指针------关联一个文件   
      FILE * 实际上是指向了一块内存空间(缓存,fileno)
           
      
      
      FILE *fp <->【FILE结构体[缓存,fileno]】//堆区(malloc)
                          |      
[用户空间]                |   
--------------------------|-------------
[内核空间]                  |
                     fileno (文件描述符)                                      
                          \----------------------------[硬盘]                    
                                                         |
                                                         |------->hello.c
                                                

      注:标准IO将对文件的操作转换成了对"流"的操作。

6. 标准IO细节

-标准io的概念 
    1975 Dennis r IO库,
    从C语言的标准,ANSI c 
    IO  input  output
    I: 键盘是标准输入设备 ====》默认输入就是指键盘  /dev/input
    O: 显示器是标准输出设备 ==》默认输出就是指显示器
    
    Linux操作系统当中IO都是对文件的操作
    C一部分,任何支持标准C的系统都可使用标准IO实现文件存储
    标准IO在UNIX上是对文件IO的封装
   
    一般都是对普通文件操作是一种有缓存的IO 在文件IO和用户程序之间,
    加入缓冲区,可以有效减少系统调用的次数,节省系统IO调度资源
    
    说明:
    标准IO库,不单单是linux上有,在windows,Mac os上都有。
    很多操作系统都实现了标准IO库。
    都是依据IOS C标准实现的。
    所以基本保证了可移植性。
    但是因为标准和具体实现之间的差异,
    未必敢保证所有的函数在都可以相互通用。
    

-标准IO都干了些啥?
    标准IO处理了很多细节:
    (1).处理缓冲区分配   (缓存--提高效率 --- 慢速 快速)
    (2).读写IO的块长度的优化
    (3).对系统调用进行了封装,内部对应的"文件描述符" 
    
    好处:
        用户使用方便,不必再担心如何选择正确的块长度。
        
    //地位:
        标准I/O库是由Dennis Ritchie在1975年左右编写的。
        它是Mike Lesk编写的可移植I/O库的主要修改版本。
        令人惊讶的是,35年来,几乎没有对标准I/O库进行修改。

- 缓冲机制:行缓冲、全缓冲、无缓冲。

行缓冲,1k, terminal,主要用于人机交互stdout
    缓存区满或者遇到\n刷新    1024
                行缓存多是关于终端的一些操作
                1.遇到\n刷新
                2.缓存区满刷新
                3.程序结束刷新
                4.fflush刷新  fflush(stdout);
                
全缓冲,4k,主要用于文件的读写
    缓存区满刷新缓存区 4096
            对普通文件进行标准IO操作,建立
            的缓存一般为全缓存
            刷新条件:
                1.缓存区满刷新
                2.程序结束刷新
                3.fflush来刷新  fflush(fp);
无缓冲,0k  主要用于出错处理信息的输出 stderr 
    不对数据缓存直接刷新
    printf();==>>stdout 
    fprintf(strerr,"fopen error %s",filename);
                界面交互    出错处理
使用gdb查看,FILE结构体,或使用写入数据测试缓冲区。
缓冲区的大小是可以设置

- 默认流指针:`stdin`——标准输入

                       `stdout`——标准输出

                       `stderr`——标准出错

7. 标准IO函数

- 按字符读写:`fgetc`、`fputc`。

面试题:

fgetc()/fputc()面试题:
如何用最短的代码实现用户任意输入并打印输出。
要求用fgetc/fputc函数实现。

while(1) 
    fputc(fgetc(stdin),stdout);

//注意点:
fgetc()函数的结束判断。


1、EOF 宏来判断 ===》系统预制的文件结束标记

end of file  -1

c = fgetc(fp);
   if(EOF == c) ///file end;
   if(!feof(fp))
   
   while(1)
   {
        c=fgetc(fp);
        if(feof(fp))
           break;    
   }

2、feof() 函数判断

int feof(FILE *stream);
功能:
      判断当前参数stream的文件流指针是否到达文件结尾。
      如果到达文件结尾则返回真,否则返回假
      注意:该操作一定要在一次IO操作之后判断。
参数:
      stream 要判断结尾的文件流对象
返回值:成功到达结尾是 真
        否则 是假

- 按行读写:`fgets`、`fputs`。

fgets原型:   

char buf[5]={0};
    fgets(buf,5,stdin);
char *fgets(char *s, int size, FILE *stream);
功能:
      从stream流对象关联的文件中获取size大小字节的文本数据
      并存储到s对应的本地内存(栈区数组,堆区内存)
      
参数: s 要存储数据的本地内存
       size 要获取的数据长度,单位字节。
       stream 要获取的目标文件流对象,
        可以是stdin ,程序会阻塞等待
        如果是普通文件fp 则指向文件第一行数据
返回值:成功 返回指向有效数据的首地址,一般等于s的地址
        失败 或者 文件末尾 NULL;


fgets读取结束的条件:
1.EOF 文件结束 
2.\n  读到 "换行符" 则读取结束 
  注意:
    会被保存到 buffer(保存数据的这块内存中)
3.size-1 个字符 
  '\0' //按字符串读写 

fputs原型:

int fputs(const char *s, FILE *stream);
功能:
       从s所在的本地内存中获取一行数据,
       并写入stream对应的文件流对象。
参数: s 要写的信息,一般是固定的字符串或者有数据的数组。
       stream 要写入的目标文件流对象

返回值:成功 nonnegative number on success
        失败 -1;

gets(buf); //危险 

   gets和fgets的区别:
            1.gets是危险的,因为没有规范读
              到数据的上限
            2.gets会去掉从终端读入的\n字符
            3.fgets会读到n个数据,如果n个
              数据中存在\n字符则立即停止当
              前的读取操作
            4.fgets不会去掉从流中读到的\n字
            符

- 二进制文件读写:`fread`、`fwrite`。

读:

size_t  fread(void  *ptr,  size_t size, size_t nmemb, FILE *stream);
功能:从指定的stream流对象中获取nmemeb个大小为size字节的数据块到ptr
      所在的本地内存中。
参数:ptr 要存储数据的本地内存一般是数组或者结构体指针
      size 单个数据块的元数据大小。最小单元的大小
      nmemb 要获取的数据块的个数,拷贝的数据块个数。
      stream 要获取数据的源文件流对象,如果是stdin表示从
      键盘获取数据,如果是fp文件则表示从普通文件获取。
返回值:
        成功 小于等于nemeb的整数,表示获取的数据长度
        失败 小于0,结尾 0;

写:

size_t fwrite(const void  *ptr,  size_t  size,size_t nmemb, FILE *stream);
功能:从ptr所在本地内存中取出nmemb个大小为size的数据块写入到stream流对应
     的文件流对象中。
参数:ptr 要写的数据块地址,一般是数组或者结构体指针
      size  要写的数据块元数据大小,单位是字节
      nmemb 要写的数据块的个数
      stream 要写的目标文件流对象。如果是stdout则表示数据会
      写到终端屏幕显示,如果是fp的普通文件则会写入到文件中。

返回值:成功 小于等于nmemb 的个数。
        失败 <0

- 文件定位:`fseek`、`ftell`、`rewind`。

int fseek(FILE *stream, long offset, int whence);

功能:将stream流文件中的文件指针从whence位置开始
      偏移offset字节的长度。
参数:stream  要移动文件指针的目标文件流对象。
      注意:不支持设备文件,一般用于普通文件。
      offset  要在文件内偏移的距离,单位字节。
                如果值为整数,则向文件末尾偏移
              如果值为负数,则向文件开头偏移
      whence  偏移的起始位置,由系统定义的三个宏开始。
          SEEK_SET  文件的开头位置 
        SEEK_CUR  文件的当前位置
        SEEK_END  文件的末尾位置

返回值:    
    成功: 返回 0
    失败:  -1;

如果从文件的指定位置向后偏移过程中已经超过了文件
的当前末尾位置,则会自动以'\0'来填充文件内容,从
而形成一种被称为"空洞文件" 的特殊文件。

练习:
    分别用不同的起始位置和不同的文件打开方式
    测试文件的偏移操作,并查找存在的问题。
    1、a/a+ 方式打开文件,fseek无效
    2、fseek函数可能执行失败,则数据默认添加文件末尾。


rewind()  等效于:

fseek(stream,0L,SEEK_SET);
long ftell(FILE *stream);
rewind(fp);
功能:
     获取当前文件流指针的具体位置,
     一般以文件开头到当前指针的字节数为返回值。
     
参数:stream 要返回指针距离的文件流对象
返回值:成功 获取到的距离长度,单位是字节
        失败 -1;
 


 

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值