前言
对文件进行操作有打开文件、关闭文件、读写文件。
文件指针:每打开一个文件,就返回一个指针(FILE *类型),称为文件指针。这个指针指向了这个文件相关的所有信息,可以用这个指针代表这个文件,通过这个指针可以对这个打开的文件进行各种操作。
缓冲区:输入输出的数据并不是直接到电脑内存和显示器中,输入的数据先暂时存放在键盘缓冲区中,然后程序从该缓冲区中读取数据。输出的数据先暂时存放在输出缓冲区中,然后再把该数据输出到屏幕。
Linux中一切文件,对目录和设备的操作都是文件操作;文件分为普通文件、管道文件、目录文件、链接文件和设备文件。
普通文件:也称磁盘文件,能够进行随机数据存储;
管道文件:从一端发送数据,另一端接收数据的数据通道;
目录文件:包含了保存在目录中文件列表的简单文件;
设备文件:Linux下各种硬件设备都是文件,该类型的文件提供了大多数物理设备的接口。分为两种类型:字符型设备和块设备。字符型设备一次只能读出和写入一个字节的数据,包括调制解调器、终端、打印机、声卡及鼠标;块设备必须以一定大小的块来读出或写入数据,块设备包括CD-ROM、RAM驱动器和磁盘驱动器等。一般字符设备用于传输数据,块设备用于存储数据。
套接字文件:在Linux中,套接字也可以当作文件来进行处理。
一、文件的创建、打开与关闭
文件的创建、打开与关闭命令为:
#include <stdio.h> //头文件包含
FILE *fopen(const char *path, const char *mode); //文件名,模式
FILE *fdopen(int filds, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
int close(FILE *stream);
fopen以mode方式打开或创建文件,成功将返回一个文件指针,失败返回NULL。
fopen创建的文件的访问权限将以0666与当前umask结合来确定。
mode的可选模式列:
模式 | 读 | 写 | 位置 | 截断原内容 | 创建 |
---|---|---|---|---|---|
rb | Y | N | 文件头 | N | N |
r+b | Y | Y | 文件头 | N | N |
wb | N | Y | 文件头 | Y | Y |
w+b | Y | Y | 文件头 | Y | Y |
ab | N | Y | 文件尾 | N | Y |
a+b | Y | Y | 文件尾 | N | Y |
fdopen函数用来打开某些不能直接用fopen方式打开的文件,如管道文件和网络套接字文件。
freopen打开一个特定的文件(由path指定),并将打开后的文件与指定的流(由fp指定)关联起来。一般用来将一个指定文件打开为一个指定的流:标准输入、标准输出、标准出错。
在Linux系统中,mode中的’b’(二进制)可以去掉,但为了保持与其他系统的兼容性,建议保留。ab和a+b为追加模式,在此两种模式下,无论文件读写点定位到何处,在写数据时都将在文件末尾添加,所以比较适合多进程写同一个文件的情况下保证数据完整性。
二、读写文件
基于指针的读写函数较多,可分为数据块读写、格式化读写、单个字符读写、字符串读写。
数据块读写:
#include <stdio.h> //头文件包含
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread从文件流stream中读取nmemb个元素,写到ptr指向的内存中,每个元素具有size个字节。
fwrite从ptr指向的内存中读取nmemb个元素,写到文件流stream中,每个元素具有size个字节。
所有的文件读写函数都是从文件的当前读写点开始读写,读写完成后,当前读写点自动往后移动size *nmemb个字节。
代码如下(示例):
#include <stdio.h>
int main()
{
char buf[50] = {'h','e','l','l','o'};
FILE *p; //定义一个FILE结构体类型的指针
p = fopen("a.txt", "r+b"); //p这个指针此时就和文件a.txt关联起来
fwrite(buf, 1, 5, p); //把buf里面的内容写到p指向的文件中
char buf1[50] = {0};
fread(buf1, 1, 5, p);
printf("buf1:%s\n", buf1);
fclose(p); //关闭p代表的文件a.txt
return 0;
}
格式化读写函数原型:
#include <stdio.h>
int printf(const char *format, ... );
int scanf(const char *format, ... );
int fprintf(FILE *stream, const char *format, ... );
int fscanf(FILE *stream, const char *format, ... );
int sprintf(char *str, const char *format, ... );
int sscanf(char *str, const char *format, ... );
以f开头的将格式化后的字符串写入到文件流stream中;
以s开头的将格式化后的字符串写入到字符串str中。
读:也就是输入,表示数据从其他地方传输到内存;
写:也就是输出,表示数据从内存传输到其他地方。
单个字符读写函数(一次读写一个字符):
#include <stdio.h>
int fgetc(FILE *stream);
int fputc(int c, FILE *stream);
int getc(FILE *stream);
int putc(int c, FILE *stream);
int getchar(void);
int putchar(int c);
fgetc从文件中读取一个字符,成功后返回这个字符,读完返回EOF,失败返回-1;
getchar和putchar从标准输入/输出流中读写数据,其他函数从文件流stream中读写数据。
字符串读写的原型为:
char *fgets(char *s, int size, FILE *stream);
int fputs(const char *s, FILE *stream);
int puts(const char *s);
char *gets(char *s);
fgets和fputs从文件流stream中读写一行数据;puts和gets从标准输入输出流中读写一行数据。
fgets可以指定目标缓冲区的大小,所以相对于gets安全,但是fgets调用时,如果文件中当前行的字符个数大于size,则下一次fgets调用时,将继续读取该行剩下的字符,fgets读取一行字符时,保留行尾的换行符。
fputs不会在行尾自动添加换行符,但是puts会在标准输出流中自动添加换行符。
三、文件定位
文件定位指读取或设置文件当前读写点,所有的通过文件指针读写数据的函数都是从文件当前读写点读写数据的。
常用函数:
#include <stdio>
int feof(FILE *stream); //通常的用法为while(!feof(fp))
int fseek(FILE *stream, long offset, int whence); //设置当前读写点到偏移whence长度为offset处
long ftell(FILE *stream); //用来获得文件流当前的读写位置
void rewind(FILE *stream); //把文件流的读写位置移至文件开头,fseek(fp, 0, SEEK_SET);
feof判断是否到达文件末尾的下一个(注意:到达文件末尾之后还会做一次)
fseek设置当前读写点到偏移whence长度为offset处;
whence可以是:
SEEK_SET(文件开头0)
SEEK_CUR(文件当前位置1)
SEEK_END(文件末尾2)
ftell获取当前的读写点。
rewind将文件当前读写点移动到文件头。
四、标准输入/输出流
系统为每个进程预先打开了3个特殊的文件,对应的3个文件指针分别为:stdin(标准输入)、stdout(标准输出)、stderr(标准出错);定义在头文件<stdio.h>中。
stdin具有可读属性,默认情况下是指从键盘的读取输入,stdout和stderr具有可写属性,默认情况下是指像屏幕输出数据。
五、目录操作
改变目录或文件的访问权限为:
#include <sys/stat.h>
int chmod(const char *path, mode_t mode); //mode形如:0777
path参数指定的文件被修改为具有mode参数给出的访问权限。
获取、改变当前目录的原型为:
#include <unistd.h> //头文件
char *getcwd(char *buf, size_t size); //获取当前目录,相当于pwd命令
int chdir(const char *path); //修改当前目录,即切换目录,相当于cd命令
getcwd()函数将当前的工作目录绝对路径复制到参数buf所指的内存空间,参数size为buf的空间大小;在调用此函数时,buf所指的内存空间要足够大,若工作目录绝对路径的字符串长度超过参数size大小,则返回NULL,errno的值为ERANGE;若参数buf为NULL,getcwd()函数会依size的大小自动配置内存(使用malloc函数);若参数size也为0,则getcwd()函数会依工作目录绝对路径的字符串长度来决定所分配内存的大小,进程可以在使用完此字符串后自动利用free()函数来释放此空间;常用形式为"getcwd(NULL,0);"。
chdir()函数用来将当前的工作目录改成参数path所指的目录。
创建和删除目录的原型为:
#include <sys/stat.h> //头文件
#include <sys/types.h>
#include <unistd.h>
int mkdir(const char *pathname, mode_t mode); //创建目录,mode是目录权限
int rmdir(const char *pathname); //删除目录
获取目录信息的原型为:
#include <sys/types.h> //头文件
#include <dirent.h>
DIR *opendir(const char *name); //打开一个目录
struct dirent *readdir(DIR *dir); //读取目录的一项信息,并返回该项信息的结构体指针
void rewinddir(DIR *dir); //重新定位到目录文件的头部
void seekdir(DIR *dir, off_t offset); //用来设置目录流目前的读取位置
off_t telldir(DIR *dir); //返回目录流当前的读取位置
int closedir(DIR *dir); //关闭目录文件
读取目录信息的步骤为:
- 用opendir()函数打开目录;
- 使用readdir()函数迭代读取目录的内容,如果已经读取到目录末尾,又想重新开始都,则可以使用rewinddir()函数将文件指针重新定位到目录文件的起始位置;
- 用closedir()函数关闭目录。
opendir()函数用来打开参数name指定的目录,并返回DIR *形式的目录流,对目录的读取和搜索都要使用此返回值。失败返回NULL。
readdir()函数用来读取目录的信息,并返回一个结构体指针,该指针保存了目录相关信息。有错误发生或读取到目录文件尾则返回NULL。
dirent结构体如下:
struct dirent
{
ino_t d_ino; //inode number(此目录进入点的inode)
off_t d_off; //offset to the next dirent(目录开头到进入点的位移)
unsigned short d_reclen; //length of this record(目录名的长度)
char d_name[256]; //filename(文件名)
};
seekdir()函数用来设置目录流目前的读取位置,再调用readdir()函数时,便可以从此新位置开始读取。参数offset代表距离目录文件开头的偏移量。
telldir()函数用来返回目录流当前的读取位置。