基于文件指针的文件操作(缓冲)
linux的文件和文件描述符
linux中对目录和设备的操作都是文件操作,文件分为普通文件,目录文件,链接文件和设备文件。
普通文件:也称磁盘文件,并且能够进行随机的数据存储(能够自由seek定位到某一个位置);
管道:是一个从一端发送数据,另一端接收数据的数据通道;
目录:也称为目录文件,它包含了保存在目录中文件列表的简单文件。
设备:该类型的文件提供了大多数物理设备的接口。它又分为两种类型:字符型设备和块设备。
字符型设备一次只能读出和写入一个字节的数据,包括调制解调器、终端、打印机、声卡以及鼠标;
块设备必须以一定大小的块来读出或者写入数据,块设备包括CD-ROM、RAM驱动器和磁盘驱动器等,一般而言,字符设备用于传输数据,块设备用于存储数据。
链接:类似于Windows的快捷方式和Linux里的别名,指包含到达另一个文件路径的文件。
套接字:在Linux中,套接字也可以当作文件来进行处理。
基于文件指针的文件操作函数是ANSI标准函数库的一部分。
1文件的创建,打开与关闭
原型为:
#include <stdio.h> //头文件包含
FILE *fopen(const char *pach,const char *mode); //文件名 模式
int fclose(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 |
在Linux系统中,mode里面的’b’(二进制)可以去掉,但是为了保持与其他系统的兼容性,建议不要去掉。ab和a+b为追加模式,在此两种模式下,无论文件读写点定位到何处,在写数据时都将是在文件末尾添加,所以比较适合于多进程写同一个文件的情况下保证数据的完整性。
2 读写文件
基于文件指针的数据读写函数较多,可分为如下几组:
数据块读写:
#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 printf(const char *format, ...); //相当于 fprintf(stdout,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, ...); // eg: sprintf(buf,”the string is;%s”,str);
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); //等同于 fgetc(FILE* stream)
int putc(int c, FILE *stream); // 等同于 fputc(int c, FILE* stream)
int getchar(void); //等同于 fgetc(stdin);
int putchar(int c); // 等同于 fputc(int c, stdout);
getchar和putchar从标准输入输出流中读写数据,其他函数从文件流stream中读写数据。
字符串读写:
char *fgets(char *s, int size, FILE *stream);
int fputs(const char *s, FILE *stream);
int puts(const char *s);// 等同于 fputs(const char *s, int size, stdout);
char *gets(char *s); // 等同于 fgets(const char *s, int size,stdin);
fgets和fputs从文件流stream中读写一行数据;
puts和gets从标准输入输出流中读写一行数据。
fgets可以指定目标缓冲区的大小,所以相对于gets安全,但是fgets调用时,如果文件中当前行的字符个数大于size,则下一次fgets调用时,将继续读取该行剩下的字符,fgets读取一行字符时,保留行尾的换行符。
fputs不会在行尾自动添加换行符,但是puts会在标准输出流中自动添加一换行符。
文件定位:
文件定位指读取或设置文件当前读写点,所有的通过文件指针读写数据的函数,都是从文件的当前读写点读写数据的。
常用的函数有:
#include <stdio.h>
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将文件当前读写点移动到文件头
注:基于文件指针的文件操作请参考《C语言文件操作常用函数详解.doc》
3目录操作
改变目录或文件的访问权限
#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所指的目录
Example:
1 #include<unistd.h> 2 int main() 3 { 4 5 chdir(“/tmp”); 6 printf(“current working directory: %s\n”,getcwd(NULL,0)); 7 return 0; 8 9 }
创建和删除目录:
原型为:
#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); //关闭目录文件
读取目录信息的步骤为:
1 用opendir函数打开目录;
2使用readdir函数迭代读取目录的内容,如果已经读取到目录末尾,又想重新开始读,则可以使用rewinddir函数将文件指针重新定位到目录文件的起始位置;
3 用closedir函数关闭目录
opendir()用来打开参数name指定的目录,并返回DIR*形态的目录流,和文件操作函数open()类似,接下来对目录的读取和搜索都要使用此返回值。函数失败则返回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(目录名的长度) */
unsigned char d_type; /* type of file(所指的文件类型) */
char d_name[256]; /* filename(文件名) */
};
seekdir()函数用来设置目录流目前的读取位置,再调用readdir()函数时,便可以从此新位置开始读取。
参数offset代表距离目录文件开头的偏移量。
telldir()函数用来返回目录流当前的读取位置。
示例:返回指定目录下的文件
1 #include <stdio.h> 2 3 #include <sys/types.h> 4 5 #include <dirent.h> 6 7 8 9 int main(int argc,char *argv[]) 10 11 { 12 13 struct dirent *pDirInfo; 14 15 DIR *pDir; 16 17 if(argc < 2) 18 19 pDir = opendir("."); 20 21 else 22 23 pDir = opendir(argv[1]); 24 25 if(NULL == pDir) 26 27 { 28 29 perror("open dir fail!"); 30 31 return -1; 32 33 } 34 35 while( (pDirInfo = readdir(pDir)) != NULL ) 36 37 printf("%s\n",pDirInfo->d_name); 38 39 closedir(pDir); 40 41 return 0; 42 43 }
Example:以树形结构的形式输出指定目录下面的所有文件
1 #include <unistd.h> 2 3 #include <stdio.h> 4 5 #include <dirent.h> 6 7 #include <string.h> 8 9 #include <sys/stat.h> 10 11 #include <stdlib.h> 12 13 void printdir(char *dir, int depth)//depth为缩进空格数 14 15 { 16 17 DIR *dp = opendir(dir); 18 19 if(NULL == dp) 20 21 { 22 23 fprintf(stderr,"cannot open directory: %s\n", dir); 24 25 return; 26 27 } 28 29 chdir(dir); 30 31 struct dirent *entry; 32 33 struct stat statbuf; 34 35 while((entry = readdir(dp)) != NULL) 36 37 { 38 39 stat(entry->d_name,&statbuf); 40 41 if(S_ISDIR(statbuf.st_mode)) //是目录 42 43 { 44 45 if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0) 46 47 continue; 48 49 printf("%*s%s/\n",depth,"",entry->d_name); 50 51 printdir(entry->d_name,depth+4);//递归 52 53 } 54 55 else //是文件 56 57 printf("%*s%s\n",depth,"",entry->d_name); 58 59 //printf(“%*s”,4,”*”); 该函数表示输出“___*”,前面输出3个空格。 60 61 //如果是printf(“%*s”,4,“**”);则表示输出“__**”,前面输出2个空格。 62 63 } 64 65 chdir(".."); 66 67 closedir(dp); 68 69 } 70 71 72 73 int main(int argc, char* argv[]) 74 75 { 76 77 char *topdir, pwd[2]="."; 78 79 if (argc < 2) 80 81 topdir=pwd; 82 83 else 84 85 topdir=argv[1]; 86 87 printf("Directory scan of %s\n",topdir); 88 89 printdir(topdir,0); 90 91 printf("done.\n"); 92 93 exit(0); 94 95 }
4标准输入/输出流
在进程一开始运行,就自动打开了三个对应设备的文件,它们是标准输入、输出、错误流,分别用全局文件指针stdin、stdout、stderr表示,stdin具有可读属性,缺省情况下是指从键盘的读取输入,stdout和stderr具有可写属性,缺省情况下是指向屏幕输出数据。
示例:
1 #include <stdio.h> 2 3 #include <unistd.h> 4 5 int main() 6 7 { 8 9 char szBuf[32]; 10 11 printf("Input string:"); //向屏幕输出一字符串 12 13 fgets(szBuf,sizeof(szBuf),stdin);//从键盘读入一行字符串 14 15 fprintf(stdout,"The string is:%s",szBuf);//向屏幕输出一行字符串 16 17 return 0; 18 19 }