【UNIX环境高级编程】文件 IO 操作 - 基础函数 open close creat lseek write read 详解

博客地址 : http://blog.csdn.net/shulianghan/article/details/46980271

一. 文件打开关闭操作相关函数介绍

1. open 函数

(1) open 函数简介

open 函数解析 :

-- 函数定义 :

     #include <fcntl.h>

     int
     open(const char *path, int oflag, ...);

-- 函数作用 : 打开或者创建一个文件;

-- 返回值 : 打开文件成功, 返回文件描述符; 如果失败, 返回 -1; 返回的文件描述符是最小的未用描述符值

(2) open 函数参数简介

参数解析 :

-- const char *path : 要打开或者创建的文件名;

-- int oflag : 函数选项, 可以是多个常量进行 "或" 运算;

-- 第三参数 : 对于打开文件来说是用不到第三参数的, 如果需要创建文件, 则需要指定第三参数;

int oflag 参数必选常量解析 : 下面的三个常量必须只能且只能指定一个;

-- O_RDONLY : 打开的文件只能读取, 没有写权限;

-- O_WRONLY : 打开的文件只能写入, 没有读权限;

-- O_RDWR : 打开的文件既能读取, 也能写入, 有双权限;

int oflag 参数可选常量解析 :

-- O_APPEND : 每次写入都追加到文件末尾;

-- O_CREATE : 如果文件不存在, 就创建, 如果有这个参数, 就需要使用第三个参数来指定创建文件时的参数;

-- O_EXCL : 指定该参数, 同时指定 O_CREATE, 文件如果存在就会报错;

-- O_TRUNC : 如果文件存在, 并且有写权限的前提下, 打开时会将其内容清空, 从新写入;

-- O_NOCTTY : 如果第一个参数文件路径指向一个终端设备, 不能将该设备作为进程的控制终端;

-- O_NONBLOCK : 如果文件路径指向一个 FIFO, 特殊文件块, 字符特殊文件, 同时指定该选项, 文件的IO操作设置为非阻塞模式;

int oflag 同步参数可选常量解析 :

-- O_DSYNC : 每次 write 操作之前等待 IO 完成, 如果写操作不影响读取刚写入的数据, 则不等待文件属性被更新;

-- O_RSYNC : 读操作时等待, 直到所有的写操作都完成;

-- O_SYNC : 每次写都要等待物理 IO 操作完成, 包括 write 引起的文件属性更新; 即 数据和属性同步更新;

2. create 函数

(1) create 函数简介

create 函数简介 :

-- 函数定义 :

     #include <fcntl.h>

     int
     creat(const char *path, mode_t mode);

-- 返回值 : 返回只写打开的文件描述符, 出错返回 -1;

-- 等效函数 : open(path_name, O_WRONLY | O_CREATE | O_TRUNC, mode);

-- mode_t mode 参数 : 指定文件的所有者;

(2) create 函数局限性

create 局限性 :

-- 只写 : create 函数只能以只写方式打开创建的文件;

-- 读取新文件方法 : 先 create 创建只写文件, 再调用 close 函数, 再调用 open 方法打开文件读取文件;

-- 创建只读文件 : open(path_name, O_RDWR | O_CREATE | O_TRUNC, mode);

3. close 函数

函数简介 :

-- 函数定义 :

     #include <unistd.h>

     int
     close(int fildes);

-- 作用 : 关闭文件, 并释放 进程 加在该文件上得所有 记录锁;

-- 关于进程 : 进程终止时, 内核会自动关闭该进程中打开的所有文件, 很多情况下都会使用关闭进程隐式关闭文件;

二. 文件偏移操作相关函数介绍

1. lseek 函数

(1) lseek 函数简介

lseek 函数简介 :

-- 函数定义 :

     #include <unistd.h>

     off_t
     lseek(int fildes, off_t offset, int whence);

-- 作用 : 显式的为一个打开的文件设置偏移量;

-- 返回值 : 如果设置偏移量成功, 返回新的偏移量;

(2) 文件偏移量简介

文件偏移量 :

-- 当前文件偏移量 : 每个打开的文件都有一个当前文件偏移量, 非负整数, 从开始处计算的字节数; 读写操作都是从当前文件偏移处开始, 读写会使当前文件偏移量增加 读写的字节数;

-- 默认偏移量 : 打开一个文件时默认 当前文件偏移量 是0, 除非指定 O_APPEND 选项;

-- 偏移量的值 : 普通文件偏移量必须是非负整数; 对于某些设备文件允许存在负数偏移量, 因此判断是否可 lseek 时, 要判断返回的文件偏移量是否 == -1;

(3) int where 参数简介

where 参数简介 :

-- SEEK_SET : 将文件偏移量设置为 0 + offset;

-- SEEK_CUR : 将文件偏移量设置为 当前位移 + offset;

-- SEEK_END : 将文件偏移量设置为 文件长度 + offset;

(4) lseek 源码示例

源码示例 :

/*************************************************************************
    > File Name: fun_lseek.c
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  7/22 07:46:59 2015
 ************************************************************************/

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main(int argc, char * argv[])
{
	/*
	 * 设置标准输入文件的 "当前文件偏移量",
	 * 设置为当前的位置 + 0;
	 */
	if(lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)
		printf("lseek 结果 -1, 该文件不能lseek\n");
	else
		printf("该文件可以执行 lseek 方法\n");
	exit(0);
}

编译执行 :

localhost:file octopus$ gcc fun_lseek.c
localhost:file octopus$ ./a.out
该文件可以执行 lseek 方法

(5) 文件空洞

文件空洞形成 :

-- 文件偏移量作用 : 文件偏移量是记录在内核中, 不引起 IO 操作, 这个偏移量主要用于执行下一次的 IO 操作;

-- 空洞形成 : 如果文件偏移量大于当前文件长度, 下一次写操作会直接加长文件, 并在中间形成一个 "文件空洞";

-- 磁盘占用情况 : 文件空洞是不占用磁盘存储区的, 写入数据超出文件长度时, 新写入的数据会重新分配磁盘块, 之间的一段文件空洞不会占用磁盘空间;

(6) 文件空洞源码示例

源码 :

/*************************************************************************
    > File Name: fun_lseek2.c
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 三  7/22 08:09:56 2015
 ************************************************************************/
#include<stdio.h>
#include<fcntl.h>
#include<stdarg.h>
#include<sys/stat.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

/*
 * 为新文件定义文件权限
 * 文件权限定义在 sys/stat.h 头文件中
 *
 * S_IRUSR 文件所有者具有可读取权限
 * S_IWUSR 文件所有者具有可写权限
 * S_IRGRP 用户组具有可读取权限
 * S_IROTH 其它用户具有可读取权限
 */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
//定义字符串字符最大个数 4096
#define MAXLINE 4096
#define MAXBYTE 16412

void err_sys(const char *, ...);
static void err_doit(int, int, const char *, va_list);
void create_hole_file();
void create_nohole_file();

char* buf1 = "abcdefghijklmn";
char* buf2 = "ABCDEFGHIJKLMN";

int main(int argc, char * argv[])
{
	create_hole_file();
	create_nohole_file();
}

void create_hole_file()
{

	int fd;
	//创建文件, 如果失败返回 -1
	if((fd = creat("lseek.hole", FILE_MODE)) == -1)
		err_sys("创建文件出错");

	//该语句执行完 offset = 14
	if(write(fd, buf1, 14) != 14)
		err_sys("buf1 写入失败");

	//该语句执行完 offset = 16398
	if(lseek(fd, 16384, SEEK_SET) == -1)
		err_sys("跳转失败");

	//该语句执行完 offset = 16412
	if(write(fd, buf2, 14) != 14)
		err_sys("buf2 写入失败");

	if(close(fd) == -1)
		err_sys("关闭文件出错");
}

void create_nohole_file()
{
	int fd;
	char buf[16412];
	if((fd = open("lseek_nohole.file", O_WRONLY | O_APPEND | O_CREAT)) == -1)
		err_sys("创建没有空洞文件失败");

	for(int i = 0; i < MAXBYTE; i++)
	{
		buf[i] = ‘a‘;
	}

	if(write(fd, buf, MAXBYTE) != MAXBYTE)
		err_sys("向无空洞文件写出错误");

	if(close(fd) == -1)
		err_sys("关闭文件出错");

}

/**
 * 当系统调用出现重大错误时,
 * 打印错误日志, 终止程序执行;
 */
void err_sys(const char *fmt, ...)
{
	/*
	 * va_list 定义在 stdarg.h 头文件中, 该类型代表一组可变参数
	 * 用法 :
	 * 1. 先定义一个 va_list 类型变量, 变量指向参数指针;
	 * 2. 使用 va_start 初始化刚定义的 va_list 变量
	 * 3. 使用 va_arg 返回可变参数, va_arg 第二参数是需要返回的参数类型
	 * 4. 使用 va_end 结束可变参数获取
	 */
	va_list ap;

	/*
	 * va_start(va_list ap, last);
	 * 定义 va_start : #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
	 * 定义 _INTSIZEOF : #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
	 * INTSIZEOF 获取类型占用空间长度, 最小占用长度为 int 整数倍
	 *
	 * 该函数调用传入可变参数, 数量和类型都是可变的
	 * 初始化 va_list 可变参数类型, 之后可以传给 va_arg 去处理每个参数,
	 * 调用 va_end 表示没有后续参数, 之后 可变参数 va_list 成为一个无效参数
	 * 每调用一个 va_start 初始化可变参数, 就必须调用一个 va_end 来释放可变参数
	 */
	va_start(ap, fmt);

	//该函数主要用于打印错误信息, 注意 errno 需要导入 errno.h 头文件
	err_doit(1, errno, fmt, ap);

	//释放可变参数, 将可变参数设置为无效
	va_end(ap);
	exit(1);
}

/**
 * 打印错误信息
 * 注意实现不要放在调用的后面, 如果放在调用后面, 则需要在开始处声明
 */
static void err_doit(int errnoflag, int error, const char* fmt, va_list ap)
{
	char buf[MAXLINE];

	/*
	 * printf 函数家族
	 * printf 向标准输出流中输出一个字符串
	 * fprintf 将字符串输出到一个指定文件中
	 * sprintf 将数据写入字符串缓冲区
	 * snprintf 将可变参数格式化并复制到一个字符串中
	 * asprintf 增强版的 sprintf
	 *
	 * vprintf 向标准输出流中输出一个可变参数
	 * 前面加上 v 说明输入是一个可变参数
	 *
	 * vsnprintf 将可变参数 ap 使用 fmt 可变参数的首地址 放入到 buf 中;
	 */
	vsnprintf(buf, MAXLINE, fmt, ap);
	if(errnoflag)
		/*
		 * snprintf(char * restrict str, size_t size, const char * restrict format, ...);
		 * 将 format 可变参数的 size - 1 个字节写出到 str 字符串中
		 * strlen 函数计算字符串的长度
		 * strerror 根据错误码获取错误信息
		 *
		 * 作用 : 将错误信息添加到之前的 buf 字符串后面
		 */
		snprintf(buf + strlen(buf), MAXLINE - strlen(buf), ": %s", strerror(error));
	//将 "\n" 添加到 buf 后面, 并覆盖 buf 的 ‘\0‘, 并在合并的字符串后面添加 ‘\0‘
	strcat(buf, "\n");
	//清除读写缓冲区, 立即将输入输出缓冲区中得数据物理写入
	fflush(stdout);
	//向指定文件写入一个字符串
	fputs(buf, stderr);
	//清除读写缓冲区
	fflush(NULL);
}

执行结果 : 由于本人使用的时 mac 操作系统, 暂时无法看出空洞效果, 可能是平台原因;

octopus-2:file octopus$ ls
fun_lseek.c		fun_lseek_hole.c
octopus-2:file octopus$ gcc fun_lseek_hole.c
octopus-2:file octopus$ ./a.out
octopus-2:file octopus$ ls -ls lseek.hole lseek_nohole.file
40 -rw-r--r--  1 octopus  staff  16398  7 27 06:57 lseek.hole
40 ----------  1 octopus  staff  16412  7 27 06:57 lseek_nohole.file

二. 文件读写操作相关函数介绍

1. read 函数

函数简介 :

-- 函数内容 :

     #include <sys/types.h>
     #include <sys/uio.h>
     #include <unistd.h>

     ssize_t
     read(int fildes, void *buf, size_t nbyte);

-- 作用 : 从 fildes 代表的文件中, 读取 nbyte 个函数到 buf 缓冲区中, 读取到得字节数可能少于 nbyte;

-- 返回值 : 如果 read 操作成功, 返回读取到得字节数, 如果失败, 返回 -1;

2. write 函数

函数简介 :

-- 函数内容 :

     #include <unistd.h>
     ssize_t
     write(int fildes, const void *buf, size_t nbyte);

-- 函数作用 : 将 buf 字符串的前 nbyte 个字节数据写入 files 文件标示符 代表的文件中;

-- 返回值 : 若成功, 返回已写的字节数, 如果失败返回 -1;

3. write read 函数示例

源码示例 :

/*************************************************************************
    > File Name: fun_read.c
    > Author: octopus
    > Mail: octopus_truth.163.com
    > Created Time: 一  7/27 07:09:36 2015
 ************************************************************************/

#include <stdio.h>
#include <sys/types.h>			// ... read() 头文件
#include <sys/uio.h>			// ... read() 头文件
#include <unistd.h>				// ... read() write() 函数头文件
#include <stdarg.h>				// va_list 可变参数操作头文件
#include <string.h>				// strlen strcat 方法的头文件
#include <errno.h>				// errno 的头文件
#include <stdlib.h>				// exit() 方法的头文件
#include <fcntl.h>				// open() 函数的头文件

#define MAXLINE 4096
#define MAXWORD 20

void err_sys(const char *fmt, ...);

int main(int argc, char * argv[])
{
	char *buf = "abcdefg\n";
	char buf_read[MAXWORD];
	int fd;

	int creat_result;
	int write_size;
	int close_result;
	int read_size;

	//创建一个文件, 使用打开方式, 如果文件不存在, 就重创建并打开
	if( ( fd = open("file_read_write.file", O_WRONLY | O_CREAT | O_TRUNC) ) == -1)
		err_sys("创建文件出错");

	//向文件中写出数据
	if( (write_size = write(fd, buf, strlen(buf))) == -1)
		err_sys("向文件写出数据出错");

	if( (close_result = close(fd)) == -1)
		err_sys("关闭文件出错");

	if( (fd = open("file_read_write.file", O_RDONLY)) == -1)
		err_sys("打开文件出错");

	//从文件中读取文件内容
	if( (read_size = read(fd, buf_read, strlen(buf)) ) == -1)
		err_sys("读取文件出错");

	if( (close_result = close(fd)) == -1)
		err_sys("关闭文件出错");

	printf("文件中得内容 : %s \n", buf_read);
}

static void err_doit(int errnoflag, int error, const char* fmt, va_list ap)
{
	char buf[MAXLINE];

	//将 ap 可变参数使用 fmt 格式, 放置 MAXLINE 个字符到 buf 缓冲中
	vsnprintf(buf, MAXLINE, fmt, ap);
	/*
	 * 如果需要错误信息, 根据错误号获取标准错误信息, 将该信息添加到 buf 缓冲中
	 * strlen 作用 : 获取字符串长度
	 * strerror 作用 : 根据错误号获取错误信息
	 */
	if(errnoflag)
		snprintf(buf + strlen(buf), MAXLINE - strlen(buf), ": %s", strerror(errno));
	//在 buf 字符串后添加换行符号
	strcat(buf, "\n");
	//刷新标准输出流
	fflush(stdout);
	//将标准错误输出添加到 buf 缓冲区中
	fputs(buf, stderr);
	//刷新所有缓冲区
	fflush(NULL);
}

void err_sys(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	err_doit(1, errno, fmt, ap);
	va_end(ap);
	exit(0);
}

执行结果 :

octopus-2:file octopus$ ls
fun_lseek.c		fun_lseek_hole.c	fun_read_write.c
octopus-2:file octopus$ gcc fun_read_write.c
octopus-2:file octopus$ sudo ./a.out
Password:
文件中得内容 : abcdefg

octopus-2:file octopus$ ls
a.out			file_read_write.file	fun_lseek.c		fun_lseek_hole.c	fun_read_write.c
octopus-2:file octopus$ 

博客地址 : http://blog.csdn.net/shulianghan/article/details/46980271

博客地址 : http://blog.csdn.net/shulianghan/article/details/46980271

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-26 07:03:07

【UNIX环境高级编程】文件 IO 操作 - 基础函数 open close creat lseek write read 详解的相关文章

unix环境高级编程——文件操作

一.阻塞io操作和非阻塞io操作 对于阻塞io操作:当cpu得不到需要的文件数据的时候,则将自己挂起,直到有了数据,才能执行下面的操作. 非阻塞io:  当cpu得不到文件时候,其一直在轮询cpu,直达得到数据. 二.文件描述符 对于内核而言,所有打开的文件,都用文件描述符来引用.所有的文件操作都是由内核态完成的,当一个用户的进程创建一个文件时候,内核会给其返回一个文件描述符. 而当用户进程,对文件进行各类读写操作的时候,也是给内核传入文件描述符.这样,就可以对其文件进行操作了. 三.open函

Unix 环境高级编程-----文件操作函数

1. open()   O_RONLY O_WONLY O_RDWR 返回值为 文件描述符fd 2. creat()   创建新文件,这个函数的产生是因为最开始open函数没有O_CREAT 功能,所以单独开发的该函数 注:不是create,没有e 3. read()  读取fd对应的文件内容 4. write() 写信息到fd对应的文件中 5. lseek()  设置当前文件偏移量 SEEK_SET, SEEK_CUR, SEEK_END 6. close()  关闭fd对应的描述符 小结:

UNIX 环境高级编程 文件和目录

函数stat , fstat , fstatat , lstat stat函数返回与此文件有关的信息结构. fstat函数使用已打开的文件描述符(而stat则使用文件名) fstatat函数 为一个相对于当前打开目录的路径名返回文件信息. lstat函数返回该符号链接的有关信息,而不是该符号链接引用的文件的信息. 使用stat最多的地方可能就是 ls -l 命令. st_mode  与 S_IFMT 进行 与 运算 在与 S_IFXXX常量相比较,来判断类型. 文件类型 1.普通文件: 无论是文

unix环境高级编程——文件i/o

一.文件描述符 对于内核,通过文件描述符来管理文件.什么是文件描述符? 在unix中,用open或者create建立几个文件时候,内核向进程返回一个整数,用来记录此文件. 以后对文件进行操作的时候,就用此文件描述符做引用. 二.open函数 open函数用于建立一个文件,函数返回文件描述符. [cpp] view plaincopy #include <fcntl.h> int open(const char *pathname, int flag); int open(const char 

unix环境高级编程——标准IO

1.对于系统调用中的IO,是针对文件描述符的.而对于标准的IO是针对流的. 标准的IO函数返回一个指向FILE的指针. 2.打开流 FILE *f; f = fopen("./a.txt",wr+); 通过上面得到打开文件的指针,以后的操作可以根据此指针来进行. 3.读和写数据流 (1)每次读一个字符的IO:  getc(f)  fgetc(f)  getchar() getchar():表示从标准输入中获得字符 char c; FILE *f; f = fopen("./a

UNIX环境高级编程 文件I/O

大多数文件I/O 只需要用到 5个函数 :    open , read , write , lseek , close 本章描述的都是不带缓冲的I/O(read write 都调用内核中的一个系统调用) 文件描述符 对于内核而言,所有文件都通过文件描述符引用. 文件描述符是一个非负整数 打开或创建一个新文件时,内核都将返回一个文件描述符 文件描述符的范围是 0~OPEN_MAX-1 函数 open 和 openat 调用 open 或 openat 可以打开或创建一个文件 int open(

UNIX环境高级编程 标准IO库

标准I/O库处理很多细节,使得便于用户使用. 流和 FILE 对象 对于标准I/O库,操作是围绕 流(stream)进行的.当用标准I/O打开或创建一个文件时,我们已使一个流与一个文件相关联. 对于ASCII字符集,一个字符用一个字节表示.对于国际字符集,一个字符可用多个字节表示.流的定向决定所读,写的字符是单字节还是多字节. 一个流在创建的最初,并没有定向,如果对未定向的流使用多字节I/O函数,则将该流定向为款定向的,反之如果使用单字节I/O函数,则将流的定向设为字节定向的. 改变流的定向的函

1、《UNIX环境高级编程》随记——基础知识

一.源码编译 1.源码下载 2.本人linux版本 Ubuntu 14.04 (GUN/Linux 3.13.0-0-32-generic x86_64) 3.前提准备 $sudo apt-get install gcc $sudo apt-get install libbsd-dev $cd apue.3e make ..等待编译完成 二.实例随笔 1.作者Makefile 以第一章第一个ls为例(apue.3e/intro/ls1.c) 第一个Makefile(apue.3e/Makefil

UNIX环境高级编程---标准I/O库

前言:我想大家学习C语言接触过的第一个函数应该是printf,但是我们真正理解它了吗?最近看Linux以及网络编程这块,我觉得I/O这块很难理解.以前从来没认识到Unix I/O和C标准库I/O函数压根不是一码事.Unix I/O也叫低级I/O,也叫Unbuffered I/O,是操作系统内核部分,也是系统调用:而C标准I/O函数相对也成Buffered I/O,高级I/O,一般是为了效率考虑对这些系统调用的封装.以前使用getchar()经常为输入完后的回车而出错.那是不理解标准I/O实现时的