Linux文件编程(二)

1.O_NONBLOCK

  • 阻塞与非阻塞

(1)如果一个函数是 阻塞式 的,则我们调用这个函数时当前进程有可能被卡住(阻塞住,实质是这个函数内部要完成的事情条件不具备,当前没法做,要等待条件成熟),函数被阻塞住了就不能立刻返回;如果一个函数是 非阻塞式 的那么我们调用这个函数后一定会立即返回,但是函数有没有完成任务不一定。
(2) 阻塞 非阻塞 是两种不同的设计思路,并没有好坏。总的来说, 阻塞式 的结果 有保障 但是 时间没保障 非阻塞式 的时间有保障但是结果 没保障。
(3)操作系统提供的API和由API封装而成的库函数,有很多本身就是被设计为阻塞式或者非阻塞式的,所以我们应用程度调用这些函数的时候心里得非常清楚。
(4) 我们打开一个文件默认就是阻塞式的 ,如果你希望以非阻塞的方式打开文件,则flag中要加O_NONBLOCK标志。
(5)只用于设备文件,而不用于普通文件。

2.O_SYNC

(1)write阻塞等待底层完成写入才返回到应用层。
(2)无 O_SYNC write只是将内容写入底层缓冲区即可返回 ,然后底层(操作系统中负责实现open、write这些操作的那些代码,也包含OS中读写硬盘等底层硬件的代码)在合适的时候会将buf中的内容一次性的同步到硬盘中。 这种设计是为了提升硬件操作的性能和销量,提升硬件寿命 ;但是有时候我们 希望硬件不好等待,直接将我们的内容写入硬盘中,这时候就可以用O_SYNC标志。

3.文件读写的一些细节

  • errno和perror

(1) errno 就是 error number 意思就是错误号码 。linux系统中对各种常见错误做了个编号,当函数执行错误时, 函数会返回一个特定的errno编号来告诉我们这个函数到底哪里错了。
(2)errno是由OS来维护的一个全局变量,任何OS内部函数都可以通过设置errno来告诉上层调用者究竟刚才发生了一个什么错误。
(3) errno本身实质是一个int类型的数字,每个数字编号对应一种错误。 当我们只看errno时只能得到一个错误编号数字(譬如-37),不适应于人看。
(4) linux系统提供了一个函数perror(意思print error) ,perror函数内部会读取errno并且将这个不好认的数字直接给转成对应的错误信息字符串, 然后print打印出来。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>



int main(int argc, char *argv[])
{
    int fd = -1;        // fd 就是file descriptor,文件描述符
    char buf[100] = {0};
    char writebuf[20] = "l love linux";
    int ret = -1;
    
    // 第一步:打开文件
    //fd = open("a.txt", O_RDWR | O_CREAT | O_EXCL, 0666);
    fd = open("a.txt", O_RDONLY);
    if (-1 == fd)        // 有时候也写成: (fd < 0)
    {
        //printf("\n");
        perror("文件打开错误");
        // return -1;
        _exit(-1);
    }
    else
    {
        printf("文件打开成功,fd = %d.\n", fd);
    }

#if 1    
    // 第二步:读写文件
    // 写文件
    ret = write(fd, writebuf, strlen(writebuf));
    if (ret < 0)
    {
        //printf("write失败.\n");
        perror("write失败");
        _exit(-1);
    }
    else
    {
        printf("write成功,写入了%d个字符\n", ret);
    }
#endif


#if 0
    // 读文件
    ret = read(fd, buf, 5);
    if (ret < 0)
    {
        printf("read失败\n");
        _exit(-1);
    }
    else
    {
        printf("实际读取了%d字节.\n", ret);
        printf("文件内容是:[%s].\n", buf);
    }
#endif    

    // 第三步:关闭文件
    close(fd);
    
    _exit(0);
}

4.read和write的count

(1)count和返回值的关系。 count参数表示我们想要写或者读的字节数,返回值表示实际完成的要写或者读的字节数 。实现的有可能等于想要读写的,也有可能小于(说明没完成任务)
(2) count再和阻塞非阻塞结合起来,就会更加复杂。如果一个函数是阻塞式的,则我们要读取30个,结果暂时只有20个时就会被阻塞住,等待剩余的10个可以读。
(3)有时候我们写正式程序时,我们要读取或者写入的是一个很庞大的文件(譬如文件有2MB),我们不可能把count设置为2*1024*1024,而应该去把count设置为一个合适的数字(譬如2048、4096),然后通过多次读取来实现全部读完。

5.文件IO效率和标准IO

(1)文件IO 指的是open、close、write、read等API函数构成的一套用来读写文件的体系,这套体系可以很好的完成文件读写,但是效率并不是最高的。
(2) 应用层C语言库函数 提供了一些用来做文件读写的函数列表,叫 标准IO 。标准IO由一系列的C库函数构成( fopen、fclose、fwrite、fread ), 这些标准IO函数其实是由文件IO封装而来的 (fopen内部其实调用的还是open,fwrite内部还是通过write来完成文件写入的)。标准IO加了封装之后主要是为了在应用层添加一个缓冲机制,这样我们通过fwrite写入的内容不是直接进入内核中的buf,而是先进入应用层标准IO库自己维护的buf中,然后标准IO库自己根据操作系统单次write的最佳count来选择好的时机来完成write到内核中的buf(内核中的buf再根据硬盘的特性来选择好的实际去最终写入硬盘中)。

6.linux系统如何管理文件

  • 硬盘中的静态文件和inode(i节点)

(1)文件平时都在存放在硬盘中的, 硬盘中存储的文件以一种固定的形式存放的 ,我们叫 静态文件。
(2)一块硬盘中可以分为 两大区域 :一个是 硬盘内容管理表项 另一个是真正存储内容的区域 。操作系统访问硬盘时 是先去读取硬盘内容管理表 ,从中找到我们要访问的那个文件的扇区级别的信息,然后再用这个信息去 查询真正存储内容的区域 ,最后得到我们要的文件。
(3) 操作系统最初 拿到的信息是 文件名 ,最终得到的是文件内容。第一步就是去查询硬盘内容管理表,这个管理表中以文件为单位记录了各个文件的各种信息, 每一个文件有一个信息列表 (我们叫inode,i节点, 其实质是一个结构体, 这个结构体有很多元素,每个元素记录了这个文件的一些信息,其中就包括文件名、文件在硬盘上对应的扇区号、块号那些东西·····)
强调:硬盘管理的时候是以文件为单位的,每个文件一个inode,每个inode有一个数字编号,对应一个结构体,结构体中记录了各种信息。
(4)联系平时实践,大家格式化硬盘(U盘)时发现有:快速格式化和底层格式化。快速格式化非常快,格式化一个32GB的U盘只要1秒钟,普通格式化格式化速度慢。这两个的差异?其实快速格式化就是只删除了U盘中的硬盘内容管理表(其实就是inode),真正存储的内容没有动。这种格式化的内容是有可能被找回的。
  • 内存中被打开的文件和vnode(v节点)

(1) 一个程序的运行就是一个进程 ,我们在程序中打开的文件就属于 某个进程 。每个进程都有一个数据结构用来记录这个进程的所有信息( 叫进程信息表 ),表中有一个 指针会指向一个文件管理表 ,文件管理表中记录了当前进程打开的所有文件及其相关信息。 文件管理表中用来索引各个打开的文件的index就是文件描述符fd ,我们最终找到的就是一个已经被打开的文件的 管理结构体vnode
(2)一个vnode中就记录了一个被打开的文件的各种信息,而且我们只要知道这个文件的fd,就可以很容易的找到这个文件的vnode进而对这个文件进行各种操作。

7.文件与流的概念

(1)流(stream)对应自然界的水流。文件操作中,文件类似是一个大包裹,里面装了一堆字符,但是文件被读出/写入时都只能一个字符一个字符的进行,而不能一股脑儿的读写,那么一个文件中N多的个字符被挨个一次读出/写入时,这些字符就构成了一个字符流。
(2) 流这个概念是动态的,不是静态的。
(3)编程中提到 这个概念,一般都是IO相关的。所以经常叫 IO流 文件操作时就构成了一个IO流

8.lseek详解

  • lseek函数介绍

(1) 文件指针 :当我们要对一个文件进行读写时,一定需要先打开这个文件,所以我们读写的所有文件都是 动态文件 。动态文件在内存中的形态就是文件流的形式。
(3)在动态文件中,我们会通过 文件指针 来表征这个正在操作的位置。所谓 文件指针 ,就是 我们文件管理表这个结构体里面的一个指针 。所以文件指针其实是vnode中的一个元素。这个指针表示当前我们正在操作文件流的哪个位置。这个指针不能被直接访问,linux系统用lseek函数来访问这个文件指针。
(5)read和write函数都是从当前文件指针处开始操作的,所以当我们用lseek显式的将文件指针移动后,那么再去read/write时就是从移动过后的位置开始的。

从空文件,先write写了12字节,然后read时是空的(但是此时我们打开文件后发现12字节确实写进来了)。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>



int main(int argc, char *argv[])
{
    int fd = -1;        // fd 就是file descriptor,文件描述符
    char buf[100] = {0};
    char writebuf[20] = "l love linux";
    int ret = -1;
    
    // 第一步:打开文件
    fd = open("a.txt", O_RDWR);
    //fd = open("a.txt", O_RDONLY);
    if (-1 == fd)        // 有时候也写成: (fd < 0)
    {
        //printf("\n");
        perror("文件打开错误");
        // return -1;
        _exit(-1);
    }
    else
    {
        printf("文件打开成功,fd = %d.\n", fd);
    }
    
    ret = lseek(fd, 3, SEEK_SET);
    printf("lseek, ret = %d.\n", ret);
    
    


#if 0    
    // 第二步:读写文件
    // 写文件
    ret = write(fd, writebuf, strlen(writebuf));
    if (ret < 0)
    {
        //printf("write失败.\n");
        perror("write失败");
        _exit(-1);
    }
    else
    {
        printf("write成功,写入了%d个字符\n", ret);
    }
#endif


#if 1
    // 读文件
    ret = read(fd, buf, 20);
    if (ret < 0)
    {
        printf("read失败\n");
        _exit(-1);
    }
    else
    {
        printf("实际读取了%d字节.\n", ret);
        printf("文件内容是:[%s].\n", buf);
    }
#endif    

    // 第三步:关闭文件
    close(fd);
    
    _exit(0);
}
  • 用lseek计算文件长度

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>



int cal_len(const char *pathname)
{
    int fd = -1;        // fd 就是file descriptor,文件描述符
    int ret = -1;
    
    // 第一步:打开文件
    fd = open(pathname, O_RDONLY);
    if (-1 == fd)        // 有时候也写成: (fd < 0)
    {
        //printf("\n");
        perror("文件打开错误");
        // return -1;
        return -1;
    }
    //else
    //{
        //printf("文件打开成功,fd = %d.\n", fd);
    //}
    
    // 此时文件指针指向文件开头
    // 我们用lseek将文件指针移动到末尾,然后返回值就是文件指针距离文件开头的偏移量,也就是文件的长度了
    ret = lseek(fd, 0, SEEK_END);
    
    return ret;
}



int main(int argc, char *argv[])
{
    int fd = -1;        // fd 就是file descriptor,文件描述符
    int ret = -1;
    
    if (argc != 2)
    {
        printf("usage: %s filename\n", argv[0]);
        _exit(-1);
    }
    

    printf("文件长度是:%d字节\n", cal_len(argv[1]));
    
    
    
    
    return 0;
}

用lseek构建空洞文件

(1)空洞文件就是这个文件中 有一段是空的。
(2)普通文件中间是不能有空的,因为我们write时文件指针是依次从前到后去移动的,不可能绕过前面直接到后面。
(3)我们打开一个文件后, 用lseek往后跳过一段,再write写入一段,就会构成一个空洞文件。
(4) 空洞文件方法对多线程共同操作文件是及其有用的 。有时候我们创建一个很大的文件,如果从头开始依次构建时间很长。有一种思路就是将文件分为多段,然后多线程来操作每个线程负责其中一段的写入。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>



int main(int argc, char *argv[])
{
    int fd = -1;        // fd 就是file descriptor,文件描述符
    char buf[100] = {0};
    char writebuf[20] = "abcd";
    int ret = -1;
    
    // 第一步:打开文件
    fd = open("123.txt", O_RDWR | O_CREAT);
    //fd = open("a.txt", O_RDONLY);
    if (-1 == fd)        // 有时候也写成: (fd < 0)
    {
        //printf("\n");
        perror("文件打开错误");
        // return -1;
        _exit(-1);
    }
    else
    {
        printf("文件打开成功,fd = %d.\n", fd);
    }
    
    ret = lseek(fd, 10, SEEK_SET);
    printf("lseek, ret = %d.\n", ret);
    
    
    // 第二步:读写文件
    // 写文件
    ret = write(fd, writebuf, strlen(writebuf));
    if (ret < 0)
    {
        //printf("write失败.\n");
        perror("write失败");
        _exit(-1);
    }
    else
    {
        printf("write成功,写入了%d个字符\n", ret);
    }

  

    // 第三步:关闭文件
    close(fd);
    
    _exit(0);
}

9.多次打开同一文件与O_APPEND

  • 重复打开同一文件读取

(1)一个进程中两次打开同一个文件,然后分别读取, 结果是fd1和fd2分别读。
(2)分别读说明:我们使用open两次打开同一个文件时,fd1和fd2所对应的文件指针是不同的2个独立的指针。文件指针是包含在动态文件的文件管理表中的,所以可以看出linux系统的进程中不同fd对应的是不同的独立的文件管理表。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>



int main(int argc, char *argv[])
{
    int fd1 = -1, fd2 = -1;        // fd 就是file descriptor,文件描述符
    char buf[100] = {0};
    char writebuf[20] = "l love linux";
    int ret = -1;
    
    // 第一步:打开文件
    fd1 = open("a.txt", O_RDWR);
    fd2 = open("a.txt", O_RDWR);
    
    //fd = open("a.txt", O_RDONLY);
    if ((-1 == fd1) || (fd2 == -1))        // 有时候也写成: (fd < 0)
    {
        //printf("\n");
        perror("文件打开错误");
        // return -1;
        _exit(-1);
    }
    else
    {
        printf("文件打开成功,fd1 = %d. fd2 = %d.\n", fd1, fd2);
    }
    
    while(1)
    {
        // 读文件
        memset(buf, 0, sizeof(buf));
        ret = read(fd1, buf, 2);
        if (ret < 0)
        {
            printf("read失败\n");
            _exit(-1);
        }
        else
        {
            //printf("实际读取了%d字节.\n", ret);
            printf("fd1:[%s].\n", buf);
        }
        
        sleep(1);
        
        // 读文件
        memset(buf, 0, sizeof(buf));
        ret = read(fd2, buf, 2);
        if (ret < 0)
        {
            printf("read失败\n");
            _exit(-1);
        }
        else
        {
            //printf("实际读取了%d字节.\n", ret);
            printf("fd2:[%s].\n", buf);
        }
        
    }

     

    // 第三步:关闭文件
    close(fd1);
    close(fd2);
    
    _exit(0);
}
  • 重复打开同一文件写入

(1)一个进程中2次打开同一个文件,然后分别写入,得到fd1和fd2。结果默认情况下应该是: 分别写
(2)有时候我们 希望接续写而不是分别写 ?办法就是在open时加 O_APPEND 标志即可
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>



int main(int argc, char *argv[])
{
    int fd1 = -1, fd2 = -1;        // fd 就是file descriptor,文件描述符
    char buf[100] = {0};
    char writebuf[20] = "l love linux";
    int ret = -1;
    
    // 第一步:打开文件
    fd1 = open("a.txt", O_RDWR | O_TRUNC | O_CREAT | O_APPEND, 0666);
    fd2 = open("a.txt", O_RDWR | O_TRUNC | O_CREAT | O_APPEND, 0666);
    
    //fd = open("a.txt", O_RDONLY);
    if ((-1 == fd1) || (fd2 == -1))        // 有时候也写成: (fd < 0)
    {
        //printf("\n");
        perror("文件打开错误");
        // return -1;
        _exit(-1);
    }
    else
    {
        printf("文件打开成功,fd1 = %d. fd2 = %d.\n", fd1, fd2);
    }
    


    while (1)
    {
        // 第二步:读写文件
        // 写文件
        ret = write(fd1, "ab", 2);
        if (ret < 0)
        {
            //printf("write失败.\n");
            perror("write失败");
            _exit(-1);
        }
        else
        {
            printf("write成功,写入了%d个字符\n", ret);
        }
        
        
        
        ret = write(fd2, "cd", 2);
        if (ret < 0)
        {
            //printf("write失败.\n");
            perror("write失败");
            _exit(-1);
        }
        else
        {
            printf("write成功,写入了%d个字符\n", ret);
        }
        sleep(1);
    }
    
    
    

    // 第三步:关闭文件
    close(fd1);
    close(fd2);
    
    _exit(0);
}

10.O_APPEND的实现原理和其原子操作性说明

(1)O_APPEND为什么能够将分别写改为接续写?关键的核心的东西是文件指针。分别写的内部原理就是2个fd拥有不同的文件指针,并且彼此只考虑自己的位移。但是O_APPEND标志可以让write和read函数内部多做一件事情,就是移动自己的文件指针的同时也去把别人的文件指针同时移动。(也就是说即使加了O_APPEND,fd1和fd2还是各自拥有一个独立的文件指针,但是这两个文件指针关联起来了,一个动了会通知另一个跟着动)
(2)O_APPEND对文件指针的影响,对文件的读写是原子的。
(3)原子操作的含义是 :整个操作一旦开始是不会被打断的,必须直到操作结束其他代码才能得以调度运行,这就叫原子操作。每种操作系统中都有一些机制来实现原子操作,以保证那些需要原子操作的任务可以运行。

11.文件共享的实现方式

什么是文件共享

(1)文件共享就是同一个文件(同一个文件指的是同一个inode,同一个pathname)被多个独立的读写体(几乎可以理解为多个文件描述符)去同时(一个打开尚未关闭的同时另一个去操作)操作。
(2)文件共享的意义有很多:譬如我们可以通过文件共享来实现多线程同时操作同一个大文件,以减少文件读写时间,提升效率。

文件共享的3种实现方式

(1)文件共享的核心就是怎么弄出来多个文件描述符指向同一个文件。
(2)常见的有3种文件共享的情况:第一种是同一个进程中多次使用open打开同一个文件,第二种是在不同进程中去分别使用open打开同一个文件(这时候因为两个fd在不同的进程中,所以两个fd的数字可以相同也可以不同),第三种情况是后面要学的,linux系统提供了dup和dup2两个API来让进程复制文件描述符。

12.文件描述符的复制1

  • dup和dup2函数介绍

(1)dup系统调用对fd进行复制,会返回一个新的文件描述符(譬如原来的fd是3,返回的就是4)
(2)dup系统调用有一个特点,就是自己不能指定复制后得到的fd的数字是多少,而是由操作系统内部自动分配的,分配的原则遵守fd分配的原则。
(3)dup返回的fd和原来的oldfd都指向oldfd打开的那个动态文件,操作这两个fd实际操作的都是oldfd打开的那个文件。实际上构成了文件共享。
  • 使用dup的缺陷分析

(1)dup并不能指定分配的新的文件描述符的数字,dup2系统调用修复了这个缺陷,所以平时项目中实际使用时根据具体情况来决定用dup还是dup2.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main(void)
{
    int fd1 = -1, fd2 = -1;
    
    fd1 = open("1.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd1 < 0)
    {
        perror("open");
        return -1;
    }
    printf("fd1 = %d.\n", fd1);
    
    close(1);        // 1就是标准输出stdout
    
    
    // 复制文件描述符
    fd2 = dup(fd1);        // fd2一定等于1,因为前面刚刚关闭了1,这句话就把
    // 1.txt文件和标准输出就绑定起来了,所以以后输出到标准输出的信息就
    // 可以到1.txt中查看了。
    printf("fd2 = %d.\n", fd2);
    printf("this is for test");
    
    close(fd1);
    return -1;
}

使用dup2进行文件描述符复制

(1)dup2和dup的作用是一样的,都是复制一个新的文件描述符。但是dup2允许用户指定新的文件描述符的数字。
(2)使用方法看man手册函数原型即可。
  • dup2复制的文件描述符,和原来的文件描述符虽然数字不一样,但是这连个指向同一个打开的文件。

  • 交叉写入的时候,结果是接续写(实验证明的)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main(void)
{
    int fd1 = -1, fd2 = -1;
    
    fd1 = open("1.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd1 < 0)
    {
        perror("open");
        return -1;
    }
    printf("fd1 = %d.\n", fd1);
    
   
    
    fd2 = dup2(fd1, 11);
    printf("fd2 = %d.\n", fd2);

    while (1)
    {
        write(fd1, "ssss", 2);
        sleep(1);
        write(fd2, "cccc", 2);
    }


    
    close(fd1);
    return -1;
}

12.fcntl函数介绍

  • fcntl的原型和作用

(1)fcntl函数是一个多功能文件管理的工具箱,接收2个参数+1个变参。第一个参数是fd表示要操作哪个文件,第二个参数是cmd表示要进行哪个命令操作。变参是用来传递参数的,要配合cmd来使用。
(2)cmd的样子类似于F_XXX,不同的cmd具有不同的功能。学习时只需要弄明白一个作为案例,搞清楚它怎么看怎么用就行了,其他的是类似的。其他的当我们在使用中碰到了一个fcntl的不认识的cmd时再去查man手册即可。
  • fcntl的常用cmd

(1) F_DUPFD 这个cmd的作用是复制文件描述符(作用类似于dup和dup2),这个命令的功能是从可用的fd数字列表中找一个比 arg大或者和arg一样大的数字 作为oldfd的一个复制的fd,和dup2有点像但是不同。dup2返回的就是我们指定的那个newfd否则就会出错,但是F_DUPFD命令返回的是>=arg的最小的那一个数字。
  • 使用fcntl模拟dup2

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main(void)
{
    int fd1 = -1, fd2 = -1;
    
    fd1 = open("1.txt, O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd1 < 0)
    {
        perror("open");
        return -1;
    }
    printf("fd1 = %d.\n", fd1);
    
    close(1);
    
    fd2 = fcntl(fd1, F_DUPFD, 0);
    printf("fd2 = %d.\n", fd2);

    while (1)
    {
        write(fd1, "aa", 2);
        sleep(1);
        write(fd2, "bb", 2);
    }


    
    close(fd1);
    return -1;
}

13.标准IO库介绍

  • 标准IO文件IO有什么区别

(1)看起来使用时都是函数,但是:标准IO是C库函数,而文件IO是linux系统的API
(2) C语言库函数是由API封装而来的 。库函数内部也是通过调用API来完成操作的, 但是库函数因为多了一层封装,所以比API要更加好用一些。
(3)库函数比API还有一个优势就是: API在不同的操作系统之间是不能通用的 但是C库函数在不同操作系统中几乎是一样的 所以C库函数具有可移植性而API不具有可移植性。
(4)性能上和易用性上看, C库函数一般要好一些。譬如IO ,文件IO是不带缓存的, 而标准IO是带缓存的 ,因此标准IO比文件IO性能要更高。
  • 常用标准IO函数介绍

常见的标准IO库函数有:fopen、fclose、fwrite、fread、ffulsh、fseek
  • 简单的标准IO读写文件实例

#include <stdio.h>        // standard input output
#include <stdlib.h>
#include <string.h>


#define FILENAME    "1.txt"

int main(void)
{
    FILE *fp = NULL;
    size_t len = -1;
    //int array[10] = {1, 2, 3, 4, 5};
    char buf[100] = {0};
    
    fp = fopen(FILENAME, "r+");
    if (NULL == fp)
    {
        perror("fopen");
        exit(-1);
    }
    printf("fopen success. fp = %p.\n", fp);
    
    // 在这里去读写文件
    memset(buf, 0, sizeof(buf));
    len = fread(buf, 1, 10, fp);
    printf("len = %d.\n", len);
    printf("buf is: [%s].\n", buf);

#if 0    
    fp = fopen(FILENAME, "w+");
    if (NULL == fp)
    {
        perror("fopen");
        exit(-1);
    }
    printf("fopen success. fp = %p.\n", fp);
    
    // 在这里去读写文件
    //len = fwrite("abcde", 1, 5, fp);
    //len = fwrite(array, sizeof(int), sizeof(array)/sizeof(array[0]), fp);
    len = fwrite(array, 4, 10, fp);
    printf("len = %d.\n", len);
#endif    
    
    fclose(fp);
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值