文件编程
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
系统中,所有打开的文件都对应一个文件描述符。文件描述符的本质是一个非负整数。当打开一个文件时,该整数由系统来分配。文件描述符的范围是0
至OPEN_MAX
。早期UNIX
的OPEN_MAX
为19
,即允许每个进程同时打开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 );
}