嵌入式Linux学习笔记第三天
——文件编程
嵌入式Linux文件编程有两种方式:系统调用和库函数。常用的文件操作函数有,open、read、write、lseek。通常来说,当一个进程运行时,都会自动打开三个文件:标准输入(键盘)、标准输出(屏幕)、标准出错处理(屏幕)。这三个文件对应的文件描述符分别为0、1、2.(对应的宏为STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO)。
一:系统调用:
所谓的系统调用其实就是操作系统提供给用户程序调用的一组“特殊的接口”,用户程序可以通过这个特殊的接口获得操作系统内核提供的服务。那么,为啥操作系统不直接让用户直接访问内核,还搞出个“特殊的接口”,Linux操作在安全方面考虑的比较周到,为了更好的保护内核空间,将程序运行空间分为用户空间(0-3G)和内核空间(3G-4G),它们分别运行在不同的等级上,在逻辑上是相互分离的。通常情况系,用户进程在通常情况下不允许访问内核数据,也就不能访问内核函数,它们只能操作用户数据,调用用户函数。下面介绍几个常用的函数。Creat() , open(), close(),read(), write(), lseek().
1:创建文件:int creat(const char *filename, mode_t mode)
所需头文件:#include<sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Filename为创建文件的文件名(包含路径,缺省是默认为当前路径),
Mode为创建的文件属性,S_IRUSR 可读
S_IWUSR 可写
S_IXUSR 可执行
S_IRWXU 可读可写可执行
或者是用数字表示,0无任何权限,1可执行,2可写,4可读,6可读可写;
要创建一个用户可读、可写、可执行,但是组没有权限,其他人可以读、可以执行的文件,并设置用户ID位。那么,我们应该使用的模式是1(设置用户ID)、0(不设置组ID)、7(1+2+4,读、写、执行)、0(没有权限)、5(1+4,读、执行)即10705:
参考程序:
#include <stdio.h>
#include <stblib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
viod creat_file(char *filename) {
/*创建的文件具有什么样的属性?*/
if(creat(filename,0755)<0){
printf(“creat file %sfailure!\n”,filename);
exit(EXIT_FAILURE);
} else {
printf(“creat file %ssuccess!\n”,filename);
}
}
int main(int argc,char *argv[]) {
int i;
if(argc<2){
perror("you haven'tinput the filename,please try again!\n");
exit(EXIT_FAILURE);
}
for(i=1;i<argc;i++){
create_file(argv[i]);
}
exit(EXIT_SUCCESS);
}
2:打开文件:int open(const char*pathname, flags, mode _t mode)
所需头文件:#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
pathname:被打开文件名(可包括路径名,缺省时为当前目录)
flags:文件打开的方式,参数可以通过“|”组合构成,但前3个参数不能互相重合。
O_REONLY:只读方式打开文件
O_WRONLY:可写方式打开文件
O_RDWR:读写方式打开文件
O_CREAT:如果文件不存在时就创建一个新文件,并用第三个参数为其设置权限。
O_EXCL:如果使用O_CREAT时文件存在,则可返回错误信息。这一参数可测试文件是否存在。
O_NOCTTY:使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端。
O_TRUNC:如文件已经存在,并且以只读或只写成功打开,那么会先全部删除文件中原因数据。
O+APPEND:以添加方式打开文件,在打开文件的同时,文件指针指向文件末尾。
mode _t mode:被打开文件的存取权限,为8进制表示法(调用O_CREAT才需要)。
函数返回值:成功:返回文件描述符
失败:-1
3:关闭文件:int close(int fd)
所需头文件:#include<unistd.h>
fd 为文件描述符,
函数返回值: 成功:0
出错:-1
例程:#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 inputthe 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);
}
4:读取文件 int read(int fd, void *buf,size_t length)
所需头文件:#include<unistd.h>
函数传入值
fd:文件描述符
buf:指定存储器读出数据的缓冲区
size_t length:指定读出的字节数
函数返回值:成功:读出的字节数0:已到达文件尾 -1:出错
在读普通文件时,若读到要求的字节数之前已达到文件的尾部,则返回字节数会小于希望读出的字节数。
5:写入文件:int write(int fd,void*buf,size_t length)
所需头文件:#include<unistd.h>
函数传入值:
fd:文件描述符
Buf:指定存储器写入数据的缓冲区
length:指定读出的字节数
函数返回值:成功:已写的字节数-1:出错
6:、偏移函数:int lseek(int fd,off_t offset,int whence)
所需头文件:#include<unistd.h>
#include<sys/types.h>
函数传入值:
fd:文件描述符
Offset:偏移量,每一读写操作所需要移动的距离,单位是字节的数量,可正可负(向前移,向后移)
Whence:当前位置的基点:
SEEK_SET:当前位置为文件开头,新位置为偏移量的大小
SEEK_CUR:当前位置为文件指针位置,新位置为当前位置加上偏移量
SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量大小
返回值: 成功:文件相对开头的当前位移
出错:-1
二:库函数编程
C库函数的文件操作实际上是独立于具体的操作系统平台的,不管是在DOS、
Windows、Linux还是在VxWorks中都是这些函数:
1:创建和打开: FILE *fopen(const char *filename,const char *mode)
所需头文件:#include<stdio.h>
函数传入值:
Filename:打开的文件名(包含路径,缺省时为当前路径)。
Mode:打开模式,C语言中支持的打开模式如下表
其中b用于区分二进制文件和文本文件,这一点在DOS、Windows系统中是有区分的,但Linux不区分二进制文件和文本文件。
返回值: 成功:指向file的指针
失败:NULL
2:关闭文件:int fclose(FILE *stream)
需头文件 |
#include<stdio.h> Stream:已打开的文件指针。 |
函数说明 |
fclose()用来关闭先前fopen()打开的文件。此动作会让缓冲区内的数据写入文件中,并释放系统所提供的文件资源。 |
返回值 |
成功:返回0, 错误:返回EOF并把错误代码存到errno。 |
例程: #include<stdio.h>
int main()
{
FILE *fp;
fp = fopen(“HELLO.c”,”a++”);
if(fp == NULL) return 0;
fclose(fp);
}
3:读取文件:size_t fread(void *ptr,size_t size,size_tn, FILE *stream)
所需头文件:#include <stdio.h>
函数传入值:
Ptr:存放读入数据的缓存区
Size_t size :每段读取的记录大小
Size_t n:读取的段数,
Stream:要读取的文件流(对应文件指针)
返回值: 成功:返回实际读取到的n数目
失败:EOF
例程:
#include<stdio.h>
#define nmemb 3
struct test
{
char name[20];
int size;
}s[nmemb];
main()
{
FILE * stream;
int i;
stream = fopen(“/tmp/fwrite”,”r”);
fread(s,sizeof(struct test),nmemb,stream);
fclose(stream);
for(i=0;i<nmemb;i++)
printf(“name[%d]=%-20s:size[%d]=%d\n”,i,s[i].name,i,s[i].size);
}
执行结果
name[0]=Linux! size[0]=6
name[1]=FreeBSD! size[1]=8
name[2]=Windows2000 size[2]=11
4:写入文件:size_t fwrite(const void *ptr,size_t size,size_t n, FILE * stream)
所需头文件:#include <stdio.h>
函数传入值:
Ptr:存放读入数据的缓存区
Size_t size :每段读取的记录大小
Size_t n:读取的段数,
Stream:要写入的文件流(对应文件指针)
返回值: 成功:返回实际读取到的n数目
失败:EOF
例程:
#include<stdio.h>
#define set_s (x,y){strcoy(s[x].name,y);s[x].size=strlen(y);}
#define nmemb 3
struct test
{
char name[20];
int size;
}s[nmemb];
main()
{
FILE * stream;
set_s(0,”Linux!”);
set_s(1,”FreeBSD!”);
set_s(2,”Windows2000.”);
stream=fopen(“/tmp/fwrite”,”w”);
fwrite(s,sizeof(structtest),nmemb,stream);
fclose(stream);
}
5:由文件中读取一个字符:int getc(FILE * stream);
所需头文件:#include <stdio.h>
函数传入值:
Stream:要读取的文件流(对应文件指针)
函数说明:fgetc()从参数stream所指的文件中读取一个字符。若读到文件尾而无数据时便返回EOF。
返回值: 成功:读取到的字符
失败:EOF(已到文件尾)
例程: #include<stdio.h>
main()
{
FILE *fp;
int c;
fp=fopen(“exist”,”r”);
while((c=fgetc(fp))!=EOF)
printf(“%c”,c);
fclose(fp);
}
6:由文件中写入一个字符:int fputc(int c,FILE * stream)
所需头文件:#include <stdio.h>
函数传入值:
Stream:要写入的文件流(对应文件指针)
返回值: 成功:写入的字符
失败:EOF
例程:#include<stdio.h>
main()
{
FILE * fp;
chara[26]=”abcdefghijklmnopqrstuvwxyz”;
int i;
fp= fopen(“noexist”,”w”);
for(i=0;i<26;i++)
fputc(a[i],fp);
fclose(fp);
}
7:移动文件流的读写位置:int fseek(FILE * stream,long offset,intwhence);
所需头文件:#include <stdio.h>
函数传入值:
Stream:要移动的文件流(对应文件指针)
Offset:移动的相对量
Whence为移动的参考量。为下列其中一种:
SEEK_SET从距文件开头offset位移量为新的读写位置。SEEK_CUR 以目前的读写位置往后增加offset个位移量。
SEEK_END将读写位置指向文件尾后再增加offset个位移量。
当whence值为SEEK_CUR 或SEEK_END时,参数offset允许负值的出现。
下列是较特别的使用方式:
1) 欲将读写位置移动到文件开头时:fseek(FILE*stream,0,SEEK_SET);
2) 欲将读写位置移动到文件尾时:fseek(FILE *stream,0,0SEEK_END);
函数说明:
fseek()用来移动文件流的读写位置。参数stream为已打开的文件指针,参数offset为根据参数whence来移动读写位置的位移数。
返回值: 成功:0
失败:-1
附加说明:fseek()不像lseek()会返回读写位置,因此必须使用ftell()来取得目前读写的位置。
例程:
#include<stdio.h>
main()
{
FILE * stream;
long offset;
fpos_t pos;
stream=fopen(“/etc/passwd”,”r”);
fseek(stream,5,SEEK_SET);
printf(“offset=%d\n”,ftell(stream));
rewind(stream);
fgetpos(stream,&pos);
printf(“offset=%d\n”,pos);
pos=10;
fsetpos(stream,&pos);
printf(“offset =%d\n”,ftell(stream));
fclose(stream);
}
执行结果
offset = 5
offset =0
offset=10
8:格式化输出数据至文件:int fprintf(FILE * stream, const char *format,.......);
所需头文件:#include <stdio.h>
函数说明
fprintf()会根据参数format字符串来转换并格式化数据,然后将结果输出到参数stream指定的文件中,直到出现字符串结束('\0')为止。
返回值: 成功:实际输出的字符数,
失败:-1,错误原因存于errno中。
例程:
#include<stdio.h>
main()
{
int i = 150;
int j = -100;
double k = 3.14159;
fprintf(stdout,”%d %f %x \n”,j,k,i);
fprintf(stdout,”%2d %*d\n”,i,2,i);
}
执行
-100 3.141590 96
150 150
9:格式化字符串输入:int fscanf(FILE * stream ,const char*format,....);
所需头文件:#include <stdio.h>
函数说明
fscanf()会自参数stream的文件流中读取字符串,再根据参数format字符串来转换并格式化数据。格式转换形式请参考scanf()。转换后的结构存于对应的参数内。
返回值: 成功:参数数目,
失败:-1,错误原因存于errno中。
例程:
#include<stdio.h>
main()
{
int i;
unsigned int j;
char s[5];
fscanf(stdin,”%d%x %5[a-z] %*s %f”,&i,&j,s,s);
printf(“%d %d %s\n”,i,j,s);
}
执行
10 0x1b aaaaaaaaabbbbbbbbbb /*从键盘输入*/
10 27 aaaaa
10:取得当前的工作目录:char * getcwd(char * buf,size_t size)
所需头文件:#include<unistd.h>
函数说明
getcwd()会将当前的工作目录绝对路径复制到参数buf所指的内存空间,参数size为buf的空间大小。在调用此函数时,buf所指的内存空间要足够大,若工作目录绝对路径的字符串长度超过参数size大小,则回值NULL,errno的值则为ERANGE。倘若参数buf为NULL,getcwd()会依参数size的大小自动配置内存(使用malloc()),如果参数size也为0,则getcwd()会依工作目录绝对路径的字符串程度来决定所配置的内存大小,进程可以在使用完此字符串后利用free()来释放此空间。
返回值:成功:将结果复制到参数buf所指的内存空间,
或 是返回自动配置的字符串指针。
失败:NULL,错误代码存于errno。
例程:
#include<unistd.h>
main()
{
char buf[80];
getcwd(buf,sizeof(buf));
printf(“currentworking directory : %s\n”,buf);
}
执行
current workingdirectory :/tmp