Unix环境高级编程<一>:文件I-O之读写、偏移操作

本节所说明的函数经常称之为不带缓冲区的I/O(unbuffered I/O),ANSI C提供的标准I/O库称为高级I/O,通常也称为带缓冲的I/O
文件描述符:对内核而言,所有打开文件都由文件描述符引用(ulimit -a 查看系统资源,如一个进程能打开的文件描述符个数或ulimit -n),文件描述符是一个非负整数。其中有三个默认打开的文件描述符0(STDIN_FILENO)、1(STDOUT_FILENO)、2(STDERR_FILENO)
一个系统可以支持的文件的个数用cat /proc/sys/fs/file-max查看,这其实和内存有关的

1. 打开、关闭函数

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
/*仅当创建新文件时,才使用第三个参数。*/

下面看open函数的参数:

O_RDONLY:只读打开
O_WRONLY:只写打开
O_RDWR:读、写打开
O_APPEND:写追加到文件末尾
O_CREAT:创建文件时需要,选择此项需要说明第三个参数mode
O_EXCL:如果同时指定了O_CREAT,而文件已存在,则出错。
O_TRUNC:如果此文件存在,且只读或只写打开,则将其长度截断为0
O_NONBLOCK:如果pathname指的是一个FIFO,一个块特殊文件或一个字符特殊文件,选择此项为此文件的本次打开操作和后续的I/O操作设置为非阻塞
O_SYNC:使每次write都等到物理I/0操作完成,及将数据从缓冲区同步到磁盘

由open函数返回的文件描述符一定是最小的未用描述符数字。
看一个栗子

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

/*常用,似的宏总能以你想的方式工作*/
#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

int main(void)
{
    umask(0);//避免受到shell的umask影响
    int fd;
    fd = open("test.txt", O_WRONLY | O_CREAT | O_EXCL, 0666);
    if (fd == -1)
        ERR_EXIT("open error");

    printf("open succ\n");
    return 0;
}

关于UNIX,函数出错常常返回一个负值,并会置errno,文件

#include <string.h>
char *strerror(int errnum);//此函数映射为一个出错信息字符串,并返回

#include <stdio.h>
void perror(const char* msg);//输出由msg指向的字符串

对于errno应知道两条:
(1)如果没有出错,则其值不会被一个例程清除;
(2)任何一个函数都不会将errno置为0

int close(int fd);//关闭

当一个进程终止时,内核自动关闭它所打开的文件,因此也有很多程序不close。

2. 文件读写

ssize_t read(int fd, void *buf, size_t count);
/*成功返回读到的字节数,字节数一般小于count,若已到文件尾返回为0,出错返回-1*/
ssize_t write(int fd, const void *buf, size_t count);
/*其返回值通常与参数值count的值相同,否则表示出错。write出错的一个常见原因:磁盘已写满,或超越一个给定进程的文件长度限制*/

看一个栗子,将标准输入复制到标准输出

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

#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

#define BUFFSIZE 8192
int main(void)
{
    int n;
    char buf[BUFFSIZE];

    while ( (n=read(STDIN_FILENO,buf,BUFFSIZE)) > 0)
    {
        if (write(STDOUT_FILENO,buf,n) != n)
            ERR_EXIT("write error");
        if (n < 0)
            ERR_EXIT("read error");
    }
    exit(0);
}

为什么BUFSIZE是8192呢?你可以测试不同的缓存长度,比较程序的循环次数,运行时间等。
注意:读取成功表明读取到缓存区了,但写操作成功不代表已经写到文件中了,仅仅是表示数据已经拷贝到内核缓存区了,不代表已经同步到文件中了
数据到文件的过程如:数据->内核缓存区->磁盘
延迟写:当数据往文件上写时,数据一般先复制到内核缓存区,若内核缓存区未满,则不将其排入到输出队列,而是等待 其写满或当内核需要重新用该缓存以便存放其他磁盘数据时,再将缓存排入到输出队列。延迟写减少了磁盘的读写次数,但是却降低了文件的更新速度,很可能欲写到文件的数据会丢失,因此可以用fsync函数来同步磁盘的内容。比较一下fsync和O_SYNC:当调用fsync时,它更新文件的内容,而对于O_SYNC,每次对文件调用write函数时,则会更新文件的内容。

3. lseek函数

/*成功返回新的文件位移,出错返回-1*/
off_t lseek(int fd, off_t offset, int whence);
/*
offset:位移量
whence如下
SEEK_SET:文件位移量被设置为据文件开始处offset个字节
SEEK_CUR:被设置为其当前值加offset,offset可正可负
SEEK_END:被设置为文件长度加上offset,offset可正可负
*/
off_t currpos;
currpos = lseek(fd,0,SEEK_CUR) 
/*测试文件是否可以设置位移量,如果文件描述符引用的是一个管道或FIFO,则lseek返回-1,并将errno设置为EPIPE*/

每个打开的文件都有一个与其相关联的“当前位移量”,它是一个非负整数,用于度量从文件开始处计算的字节数,通常,读写操作都从当前文件位移量开始,并使位移量增加所读或所写的字节数。按系统默认,当打开一个文件时,除非指定O_APPEND选项,否则该位移量被设置为0,注意lseek函数只修改文件的当前位移量,没有进行任何I/O操作文件位移量可以大于文件的当前长度
下面看一个栗子,空洞文件

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

/*
do ... while(0)宏定义常用,作用:使用此结构构造的宏定义不会受到大括号,分号等的影响,使得宏总是会按你的期望运行
*/
#define err_exit(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

#define FILE_MODE 0666
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int main(void)
{
    int fd;
    if ( (fd=creat("file.hole",FILE_MODE)) < 0)
            err_exit("creat error");
    if (write(fd,buf1,10) != 10)
            err_exit("buf1 write error");
    /*offset now=10*/
    if (lseek(fd,1024*1024,SEEK_SET) == -1)  
            err_exit("lseek error");
    /*offset now=40*/
    if (write(fd,buf2,10) != 10)
        err_exit("write error");
    /*offset now=50*/

    exit(0);
}

通过以下三种方式查看

#大小约为1.1M
ll -h file.hole
#大小约为8k
du -h file.hole
#od可查看到文件中的空格
od -c file.hole

一个文件的大小不等于其在磁盘上占有的空间,如空洞文件,还有,可以写几个字节到文件中,可以发现其占用磁盘的就是4k.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优化这段代码<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>图书购物车</title> <style> </style> <script src="js/vue.js"></script> </head> <body> <div id="demo"> <table border="1"> <tr> <td></td> <td>书籍名称</td> <td>出版日期</td> <td>价格</td> <td>购买数量</td> <td>操作</td> </tr> <tr> <td></td> <td>{{books1.name}}</td> <td>{{books1.date}}</td> <td>¥{{books1.price}}</td> <td><button @click="down(books1)">-</button>{{books4.count}}<button @click="up(books1)">+</button></td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books2.name}}</td> <td>{{books2.date}}</td> <td>¥{{books2.price}}</td> <td> <button @click="down(books2)">-</button>{{books4.count}}<button @click="up(books2)">+</button> </td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books3.name}}</td> <td>{{books3.date}}</td> <td>¥{{books3.price}}</td> <td> <button @click="down(books3)">-</button>{{books4.count}}<button @click="up(books3)">+</button> </td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books4.name}}</td> <td>{{books4.date}}</td> <td>¥{{books4.price}}</td> <td> <button @click="down(books4)">-</button>{{books4.count}}<button @click="up(books4)">+</button> </td> <td><button @click="del">移除</button></td> </tr> </table> <div>总价: ¥{{sum}}</div> </div> <!-- js部分 --> <script> const vm = new Vue({ el: "#demo", data: { books1: { name: '《算法导论》', date: '2006-9', price: 85.00, count: 1 }, books2: { name: '《UNIX编程艺术》', date: '2006-2', price: 59.00, count: 1 }, books3: { name: '《编程珠玑》', date: '2008-10', price: 39.00, count: 1 }, books4: { name: '《代码大全》', date: '2006-3', price: 128.00, count: 1 } }, computed: { sum () { return this.books1.price * this.books1.count + this.books2.price * this.books2.count + this.books3.price * this.books3.count + this.books4.price * this.books4.count } }, methods: { down (books1) { this.books1.count = books1.count - 1; } } }) </script> </body> </html>
最新发布
04-21

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值