本帖最后由 yuanlai2010 于 2014-8-3 10:34 编辑
Linuxprograming 文件操作
参与Helper2416开发板助学计划心得
可以这么说,Linux中的任何事物都可以用一个文件来表示,甚至硬件设备在Linux中通常也被表示为文件(设备文件)。所以,掌握如何操作文件的方法就显得尤为重要了,
在LINUX中,文件的操作可以通过两种方式实现,一种是系统调用,另外一种就是通过标准库函数来实现(最终还是通过系统调用来实现的)。
一:系统调用方式
0:文件描述符
在系统中用文件描述符来关联已经被打开的文件,我们就是通过文件描述符来实现对文件操作的,文件描述符本身是一些小值整数。
当一个程序开始运行的时候,它通常会有三个已经打开的文件描述符。
0:标准输入
1:标准输出
2:标准错误
我们可以直接通过向描述符1写内容,让它打印在终端上
1:write系统调用
系统调用write的作用是把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中。他返回实际写入的字节数。如果文件描述符有错或者底层的设备驱动程序对数据块长度比较敏感,该返回值可能会小于nbytes。如果这个函数返回--,就表示未写入任何数据;如果它返回的是-1,则表示在往write调用中出现了错误,错误代码会报讯在全局变量errno里。
- #include <unistd.h>
- size_t write(int fildes, const void *buf, size_t nbytes);
复制代码
2:read系统调用
系统调用read的作用是:从与文件描述符fildes相关联的文件里读入nbytes个字节的数据,并把他们放到数据区buf中。他返回实际读入的字节数,这可能会小于请求的字节数,如果read调用返回0,就表示未读入任何数据,已到达了文件尾。同样,如果返回的是-1,就表示read调用出现了错误
- #include <unistd.h>
- size_t read(int fildes, void *buf, size_t nbytes);
复制代码
3:open系统调用
Open建立了一条文件或设备的访问路径,如果调用成功吗,他将返回一个可以被read、write和其他系统调用使用的文件描述符(新文件总是使用未用描述符的最小值)。这个文件描述符是唯一的,他不会与任河其他运行中的进程共享。
参数path为准备打开的文件或设备的名字,oflags参数用于指定打开文件所采取的动作。
oflags参数的使用比较灵活,有必选的和可选的,它们可以组合起来一起使用,当oflags参数中存在O_CREAT的时候,还会有第三个参数mode,用来指定将被创建文件的访问权限的初始值。
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- int open(const char *path, int oflags);
- int open(const char *path, int oflags, mode_t mode);
复制代码oflags必选参数(其中之一):
O_RDONLY:以只读方式打开
O_WRONLY:以只写方式打开
O_RDWR:以读写方式打开
可选参数(用“按位或”操作组合使用)
O_APPEND:把写入数据追加在文件的末尾。
O_TRUNC:把文件长度设置为零,丢弃已有的内容
O_CREAT:如果需要,就按照参数mode中给出的访问模式创建文件
O_EXCL:与O_CREAT一起使用,确保调用者创建出文件。使用这个参数可以防止两个程序同时创建一个文件。
mode参数的值,这个与linux文件系统中的文件权限是一样的,可以用4位的八进制数来表示,权限对应如下,
r - 1
w - 2
x - 4
当mode的值为0777时,对应的权限就是 –rwxrwxrwx 在linux中用ls–l 命令就可一看到这些权限。不过文件的最终权限还与系统变量umask有关。具体就是mode值与umask的反值做AND运算。
4:close系统调用
可以使用close调用终止文件描述符filldes与其对应文件之间的关联,文件描述符被释放并能够重新使用。Close调用成功时返回0,出错时返回-1。(程序运行完系统也会帮我们自动调用此函数的,建议还是自己写上)
- #include <unistd.h>
- int close(int fildes);
复制代码
5:lseek系统调用
lseek系统调用对文件描述符fildes的读写指针进行设置。可以用它来设置文件的下一个读写位置。
- #include <unistd.h>
- #include <sys/types.h>
- off_t lseek(int fildes, off_t offset, int whence);
复制代码offset参数是用来制定位置,而whence参数定时该偏移值的用法。Whence可以取下列值之一 。
SEEK_SET:offset是一个绝对值
SEEK_CUR:offset是一个相对于当前位置的一个相对位置
SEEK_END:offset是相对于文件尾的一个相对位置
Lseek返回从文件图到文件指针被设置处的字节偏移值,失败时返回-1。
二:标准I/O库方式
这个比较熟悉,在学习C语言的时候,写的第一个程序就是在控制台打印出“hello world”
他就是通过标准I/O库(stdio)来实现的。标准I/O库(stdio)及其头文件stdio.h为底层I/O系统调用提供了一个通用的接口。所以最终还是通过系统调用来实现的,但是标准I/O库是具有输入输出缓冲的,只有当缓冲器内的内容满足数据块长度时才进行系统调用,才真正的写入文件的。
0:文件流
与系统调用中使用的文件描述符不同的是,在标准库里使用流(stream)与文件相关联的,它被实现为指向结构FILE的指针
在启动程序的时候,有三个文件流是自动打开的,他们是stdin stdout 和 stderr。与底层文件描述符0、1、2相对应。
1:fopen函数
Fopen库函数类似于底层的open系统调用,它主要用于文件和终端的输入输出。如果需要对设备进行明确的控制,最好还是使用底层系统调用。因为这样可以避免一些使用库函数的潜在问题,如输入输出缓冲。
- #include <stdio.h>
- FILE *fopen(const char *filename, const char *mode);
复制代码filename:文件名,用来指定要打开的文件。
mode:用来指定打开方式,注意该参数是个字符串
fopen在调用成功后返回一个非空的FILE *指针,失败时返回NULL
2:fread函数
fread库函数用于从一个文件刘读取数据,数据从文件刘stream读到由ptr指向的数据缓冲区里,fread和fwrite都是对数据记录进行操作,size参数指定每个数据记录的长度,计数器nitems给出数据的记录个数。它的返回值是成功读到数据缓冲区里的记录个数(注意不是字节数),当到达文件尾时,它的返回值可能会小于nitems,甚至可以是零
- #include <stdio.h>
- size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);
复制代码
3:fwrite函数
fwrite库函数与fread有相似的接口。它从指定的数据缓冲区里取出数据记录,并把他们写到输出流中。他的返回值是成功写入的记录个数
- #include <stdio.h>
- size_t fwrite(void *ptr, size_t size, size_t nitems, FILE *stream);
复制代码
4:fclose函数
fclose库函数用于关闭指定的文件流stream,是所有哦尚未写出的数据都写出。因为stdio库会对数据进行缓冲,所有使用fclose很有必要。
- #include <stdio.h>
- int fclose(FILE *stream);
复制代码
5:fflush函数
fflush库函数的作用是吧文件流里的所有未写出数据立刻写出。注意:fclose函数隐含执行了一次flush操作。
- #include <stdio.h>
- int fflush(FILE *stream);
复制代码
6:fseek函数
fseek函数是与lseek系统调用对应的文件流函数。他在文件流里为下一次操作指定位置。offset和whence参数的含义和取值与前面的lseek系统调用完全一样。但lseek返回的是一个off_t数值,而fseek返回的是一个整数:0表示成功,-1表示失败并设置errno指出错误。
- #include <stdio.h>
- int fseek(FILE *stream, long int offset, int whence);
复制代码
最后通过一个小程序来实践一下。
代码如下:
- #include <unistd.h> //必须第一个被包含
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <stdlib.h>
- #define Aimsize 8192
- /* Usage: mkbl1 <source file> <destination file> */
- int main(int argc, char *argv[])
- {
- char block[1024];
- int in, out;
- int numread, remaining, numrcd = 0;
-
- if(argc != 3){/* 判断参数是否正确 */
- write(2, "Usage: mkbl1 <source file> <destination file>\n", 46);
- /* 这里的描述符2是程序运行时自动打开的标准错误输出 */
- exit(1);
- }
-
- in = open(argv[1], O_RDONLY);
- out = open(argv[2], O_RDWR|O_CREAT, 0777);
- /* mode参数为0777 就是待变权限为 -ewxewxewx */
-
- /* 先把把bin文件先拷贝到输出文件中 */
- while((numread = read(in, block, sizeof(block))) > 0){
- numrcd += write(out, block, numread);
- }
-
- if(numrcd > Aimsize){/*r如果输出文件已经大于8KB的话则返回错误信息*/
- write(2, "The original bin is out of 8KB !\n", 33);
- close(in);
- /* 在Linux系统调用中有没有什么函数可以把文件删除吗? */
- exit(1);
- }else{/* 把输出文件补全为8KB */
- remaining = (Aimsize - numrcd)/sizeof(block);
- while(remaining--){
- write(out, block, sizeof(block));
- }
- write(out, block, (Aimsize - numrcd)%sizeof(block));
- }
-
- close(in);
- close(out);
- exit(0);
- }
复制代码
论坛ID:yuanlai2010
发表时间:2014-08-03