系统调用之文件访问

文件编程

  Linux文件编程可以使用两种方法:Linux系统调用和C语言库函数,前者依赖于Linux系统,后者与操作系统是独立的,在任何操作系统下,使用C语言库函数操作文件的方法都是相同的。

创建文件

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

参数filename是要创建的文件名(包含路径,缺省为当前路径),mode为创建模式。常见创建模式如下:

模式说明
S_IRUSR可读
S_IWUSR可写
S_IXUSR可执行
S_IRWXU可读、写、执行

  除了可以使用上述宏以外,还可以直接使用数字来表示文件的访问权限:

数字权限
1可执行
2可写
4可读
6可写可读(即4 + 2)
0无任何权限

  使用creat函数创建文件:

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

void create_file ( char *filename ) {
    if ( creat ( filename, 0755 ) < 0 ) {
        printf ( "create file %s failure!\n", filename );
        exit ( EXIT_FAILURE );
    } else {
        printf ( "create file %s success!\n", filename );
    }
}

int main ( int argc, char *argv[] ) {
    int i;
    if ( argc < 2 ) {
        perror ( "you haven't input the filename, please try again!\n" );
        exit ( EXIT_FAILURE );
    }

    for ( i = 1; i < argc; i++ ) {
        create_file ( argv[i] );
    }

    exit ( EXIT_SUCCESS );
}

文件描述

  在Linux系统中,所有打开的文件都对应一个文件描述符。文件描述符的本质是一个非负整数。当打开一个文件时,该整数由系统来分配。文件描述符的范围是0OPEN_MAX。早期UNIXOPEN_MAX19,即允许每个进程同时打开20个文件,现在很多系统则将其增加至1024

打开文件

int open ( const char *pathname, int flags );
int open ( const char *pathname, int flags, mode_t mode );

参数pathname是要打开的文件名(包含路径,缺省为当前路径),flags是打开标志。常见的打开标志:

标志说明
O_RDONLY只读方式打开
O_WRONLY只写方式打开
O_RDWR读写方式打开
O_APPEND追加方式打开
O_CREAT创建一个文件
O_NOBLOCK非阻塞方式打开

  如果使用了O_CREATE标志,则使用的函数是int open(const char *pathname, int flags, mode_t mode);,这时需要指定mode来表示文件的访问权限。

关闭文件

  当我们操作完文件以后,需要关闭文件:

int close ( int fd ); /* fd是文件描述符 */

对文件进行打开或关闭操作:

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

int main ( int argc , char *argv[] ) {
    int fd;
    if ( argc < 2 ) {
        puts ( "please input the open file pathname!\n" );
        exit ( 1 );
    }

    /* 如果flag参数里有O_CREAT,表示该文件如果不存在,系统则会创建该文件,
       该文件的权限由第三个参数决定,此处为0755。如果flah参数里没有O_CREAT参数,
       则第三个参数不起作用。此时,如果要打开的文件不存在,则会报错。
       所以“fd = open(argv[1], O_RDWR)”仅仅只是打开指定文件 */
    if ( ( fd = open ( argv[1], O_CREAT | O_RDWR, 0755 ) ) < 0 ) {
        perror ( "open file failure!\n" );
        exit ( 1 );
    } else {
        printf ( "open file %d  success!\n", fd );
    }

    close ( fd );
    exit ( 0 );
}

读文件

int read ( int fd, const void *buf, size_t length );

从文件描述符fd所指定的文件中读取length个字节到buf所指向的缓冲区中,返回值为实际读取的字节数。

写文件

int write ( int fd, const void *buf, size_t length );

length个字节从buf指向的缓冲区中写到文件描述符fd所指向的文件中,返回值为实际写入的字节数。

文件定位

int lseek ( int fd, offset_t offset, int whence );

将文件读写指针相对whence移动offset个字节。操作成功时,返回文件指针相对于文件头的位置。whence可使用下述值:

whence说明
SEEK_SET相对文件开头
SEEK_CUR相对文件读写指针的当前位置
SEEK_END相对文件末尾

offset可取负值,表示向前移动。例如下述调用可将文件指针相对当前位置向前移动5个字节:

lseek ( fd, -5, SEEK_CUR );

由于lseek函数的返回值为文件指针相对于文件头的位置,因此下面调用的返回值就是文件的长度:

lseek ( fd, 0, SEEK_END );

  对文件进行读写操作:

#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "unistd.h"
#include "stdio.h"

#define NO_DUP 0

#if NO_DUP
int main ( void ) {
    int fd;
    char *buf = "123456789";
    char buf1[10];
    fd = open ( "./test.txt", O_RDWR | O_CREAT, 0755 ); /* 打开文件 */

    if ( fd < 0 ) {
        printf ( "open file fail\n" );
    }

    write ( fd, buf, 7 ); /* 写入文件 */
    lseek ( fd, 0, SEEK_SET ); /* 重定位文件指针 */
    read ( fd, buf1, 5 ); /* 读文件 */
    buf1[5] = '\0';
    printf ( "buf1 is %s\n", buf1 );
    return 0;
}
#else
int main ( void ) {
    int fd;
    int fd1;
    char *buf = "123456789";
    char buf1[10];
    fd = open ( "./test.txt", O_RDWR | O_CREAT, 0755 ); /* 打开文件 */

    if ( fd < 0 ) {
        printf ( "open file fail\n" );
    }

    fd1 = dup ( fd );
    write ( fd1, buf, 7 ); /* 写入文件 */
    lseek ( fd1, 0, SEEK_SET ); /* 重定位文件指针 */
    read ( fd1, buf1, 5 ); /* 读文件 */
    buf1[5] = '\0';
    printf ( "buf1 is %s\n", buf1 );
    return 0;
}
#endif

文件访问判断

  有时需要判断文件是否可以进行某种操作(读、写等),这时可以使用access函数:

int access ( const char *pathname, int mode );

参数pathname是文件名称;mode是要判断的访问权限,可以取以下值或者是它们的组合:

mode说明
R_OK文件可读
W_OK文件可写
X_OK文件可执行
F_OK文件存在

当我们测试成功时,函数返回0,否则如果一个条件不符时,返回-1

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

int main() {
    if ( access ( "/etc/passwd", R_OK ) == 0 ) {
        printf ( "/etc/passwd can be read!\n" );
    }

    return 0;
}

  使用系统调用实现对文件的复制:

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

#define BUFFER_SIZE 1024

int main ( int argc, char **argv ) {
    int from_fd, to_fd;
    int bytes_read, bytes_write;
    char buffer[BUFFER_SIZE];
    char *ptr;

    if ( argc != 3 ) {
        fprintf ( stderr, "Usage:%s fromfile tofile/n/a", argv[0] );
        exit ( 1 );
    }

    if ( ( from_fd = open ( argv[1], O_RDONLY ) ) == -1 ) { /* 打开源文件 */
        fprintf ( stderr, "Open %s Error:%s/n", argv[1], strerror ( errno ) );
        exit ( 1 );
    }

    /* 创建目的文件 */
    if ( ( to_fd = open ( argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR ) ) == -1 ) {
        fprintf ( stderr, "Open %s Error:%s/n", argv[2], strerror ( errno ) );
        exit ( 1 );
    }

    /* 以下代码是一个经典的拷贝文件的代码 */
    while ( bytes_read = read ( from_fd, buffer, BUFFER_SIZE ) ) {
        if ( ( bytes_read == -1 ) && ( errno != EINTR ) ) { /* 一个致命的错误发生了 */
            break;
        } else if ( bytes_read > 0 ) {
            ptr = buffer;

            while ( bytes_write = write ( to_fd, ptr, bytes_read ) ) {
                if ( ( bytes_write == -1 ) && ( errno != EINTR ) ) { /* 一个致命错误发生了 */
                    break;
                } else if ( bytes_write == bytes_read ) { /* 写完了所有读的字节 */
                    break;
                } else if ( bytes_write > 0 ) { /* 只写了一部分,继续写 */
                    ptr += bytes_write;
                    bytes_read -= bytes_write;
                }
            }

            if ( bytes_write == -1 ) { /* 写的时候发生的致命错误 */
                break;
            }
        }
    }

    close ( from_fd );
    close ( to_fd );
    exit ( 0 );
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值