Linux文件操作-1

1. 理解文件描述符

1.1 文件描述符的概念

       文件描述符是个很小的正整数,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。例如,每个进程启动时都会打开3个文件:标准输入、标准输出和标准错误文件。这3个文件分别对应于文件描述符0、1和2。

       提示:应该使用<unistd.h>中定义的3个宏来代替数字0、1或2:STDIN_FILENO、STDOU_FILENO和STDERR_FILENO,因为你的程序可能会在一个stdin、stdout和stderr不与整数0、1、2相对应的系统上进行编译。

       许多Linux和UNIX系统调用都依赖于文件描述符。比如,低级的open、close、read和write调用都使用文件描述符。在Linux上,几乎每一样东西都是一个文件,至少描象地看是这样。这一事实也是Linux最具独创性的设计特色之一,因为它让大量的资源,比如内存、串口、伪终端、打印端口、声卡、鼠标甚至其他运行着的进程有了统一的编程接口。

 

2. 文件操作的相关系统调用

       Linux的文件操作系统调用涉及创建、打开、读写和关闭文件。

2.1 创建——调用成功后返回一个文件描述符,或者如果失败,则设置errno变量并返回-1。

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

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

       参数mode指定新建文件的存取权限,它同umask一起决定文件的最终权限(mode&umask),其中umask代表了文件在创建进需要去掉的一些存取权限。umask可通过系统调用umask()来改变,如下所示:

int umask(int newmask);

 

2.2 打开——调用成功后返回一个文件描述符,或者如果失败,则设置errno变量并返回-1。

#include <sys/types.h>             提供类型pid_t的定义

#include <sys/stat.h>

#include <fcntl.h>

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

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

       open()函数有两个形式,其中pathname是我们要打开的文件名(包含路径名称,默认时认为是在当前路径下面)。flags可以是下面的一个传开中者是几个值的组合,在fcntl.h文件中定义具体的值。

/******************include/asm-generic/fcntl.h******************/

       标志                                 含义

       O_RDONLY                       以只读的方式打开文件

       O_WRONLY                      以只写的方式打开文件

       O_RDWR                          以读写的方式打开文件

       O_CREAT                          创建一个文件

       O_EXCL                            仅与O_CREAT连用,如果文件已经存在,则强制open失败

       O_NOCTTY                       如果打开的文件是一个终端,就不会成为打开其进程的控制终端

       O_TRUNC                         如果文件已经存在,则删除文件的内容,将其长度截为0

       O_NOBLOCK                    以非阻塞的方式打开一个文件

       O_APPEND                       将文件指针设置到文件的结束处(如果打开来写),以追加的方式打开文件

       O_SYNC                            在数据被物理地写入磁盘或其他设备之后操作才返回

      

       O_RDONLY、O_WRONLY、O_RDWR这3个标志只能使用任意的一个。

       如果使用了O_CREATE标志,则使用的函数是int open(const char *pathname , int flags , mode_t mode);这个时候我们还要指定mode标志,用来表示文件的访问权限。mode可以是下面的值组合。

/******************include/linux/stat.h******************/

       标志                                 含义

       S_IRUSR                           用户可以读

       S_IWUSR                          用户可以写

       S_IXUSR                           用户可以执行

       S_IRWXU                          用户可以读、写、执行

       S_IRGRP                           组可以读

       S_IWGRP                          组可以写

       S_IXGRP                           组可以执行

       S_IRWXG                          组可以读、写、执行

       S_IROTH                           其他人可以读

       S_IWOTH                          其他人可以写

       S_IXOTH                           其他人可执行

       S_IRWXO                          其他人可以读、写、执行

       S_ISUID                            设置用户的执行ID

       S_ISGID                            设置组的执行ID

 

       除了可以通过上述宏进行“或”逻辑产生标志以外,我们也可以用数字来表示,Linux总共用了5个数字来表示文件的各种权限;第一位表示设置用户ID;第二位表示设置组ID;第三位表示用户自己的权限位;第四位表示组的权限;第五位表示其他人的权限。每个数字可以取1(执行权限)、2(写权限)、4(读权限)、0(无)或者是这些值的和。

       例如,如果要创建一个用户可读、可写、可执行,但是组没有权限,其他人可以读、可以执行的文件,并设置用户ID位。那么,应该使用的模式是1(设置用户ID)、0(不设置组ID)、7(1+2+4,读、写、执行)、0(没有权限)、5(1+4,读、执行)即10705,如下所示:

open("test" , O_CREAT , 10705);

上述语句等介于:

open("test" , O_CREAT , S_IRWXU | S_IROTH | S_IXOTH | S_ISUID);  

       如果文件打开成功,open函数会返回一个文件描述符,以后对该文件的所有操作就可以通过这个文件描述符进行操作来实现。

       以O_CREAT为标志的open函数实际上实现了文件的创建功能,因此,下面的函数等同creat()函数:

int open(pathname , O_CREAT | OWRONLY | O_TRUNC , mode);

 

2.3 文件指针转换

fileno()函数

功    能:把文件流指针转换成文件描述符
相关函数:open, fopen
表头文件:#include <stdio.h>
定义函数:int fileno(FILE *stream)
函数说明:fileno()用来取得参数stream指定的文件流所使用的文件描述符
返回值  :返回和stream文件流对应的文件描述符。如果失败,返回-1。

 

fdopen ()函数

功    能:打开一个文件流
相关函数:open, fopen, freopen
表头文件:#include <stdio.h>
定义函数:FILE *fdopen(int filedes, cosnt char *type); 

函数说明:fileno()根据参数filedes指定的文件描述符打开一个文件流
返回值  :若成功则返回文件指针,若出错则返回NULL。

 说明:fopen和fdopen都会自动创建一个FILE对象(即流对象),而freopen是重用一个流对象。对于freopen函数我们很少自己定义一个FILE对象,然后传递给它,而是通过fopen或者fdopen或者用三个预定义流stdin、stdout和stderr递给它。为什么不能自己定义一个FILE对象?因为我们不清楚一个FILE对象内部是怎么样,很难初始化它的数据成员。

 

2.4 读写——成功时,返回读写的字节大小,错误时返回-1,并设置errno变量。

       用于向文件描述符对应的文件写入数据,在文件打开之后,我们才可以对文件进行读写,Linux系统中提供文件读写的系统调用是read、write函数,如下所示:

#include <unistd.h>

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

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

       其中参数fd是以前的open调用返回的有效文件描述符,参数buf为指向缓冲区的指针,length为缓冲区的大小(以字节为单位)。函数read()实现从文件描述符fd所指定的文件中读取length个字节到buf所向的缓冲区中,返回值为实际读取的字节数。函数write实现把length个字节从buf指向的缓冲区中写到文件描述符fd所指向的文件中,返回值为实际写入的字节数。

/*

// unistd.h

#define STDIN_FILENO    0       // Standard input. 

#define STDOUT_FILENO   1       // Standard output. 

#define STDERR_FILENO   2       // Standard error output

 */

#include <stdio.h>

#include <unistd.h>

int main(void)

{

       int len;

       char buf[1024];

       while((len=read(STDIN_FILENO,buf,sizeof(buf)))>0)

       {

              if(write(STDOUT_FILENO,buf,len)!=len)

              {

                     perror("write! ");

                     return 1;

              }

       }

       if(len<0)

       {

              perror("read! ");

              return 1;

       }

       return 0;

}      

 

2.5 定位

       对于随机文件,我们可以随机地指定位置读写,使用如下函数进行定位:

#include <unistd.h>

#include <sys/type.h>

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

       lseek()将文件读写指针相对whence移动offset个字节。操作成功时,返回文件指针相对于文件头的位置。失败时,返回(offset)-1,并设置error参数,文件的偏移量不变。参数whence可以使用如下值:

SEEK_SET:当前位置为文件开头,新位置为偏移量的大小。

SEEK_CUR:当前位置为文件读写指针的位置,新位置为当前位置加上偏移量。

SEEK_END:当前位置为文件末尾,新位置为文件的大小加上偏移量的大小。

       移动到文件的文件开头:

lseek(fd , 0 , SEEK_SET);

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

lseek(fd , -5 , SEEK_CUR);

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

lseek(fd , 0 , SEEK_END);


例程:读取文件test.dat的内容,并判断有字符simple几次
/*name: lseek.test.c*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc , char *argv[])
{
 //len将用于保存输入要查询的字符串长度
 int len;
 //fd用于保存打开文件描述符
 int fd;
 //offset为文件偏移量
 long offset=0;
 //buffer用于存储读出的文件内容
 char buffer[1024];
 //flag用于统计查找到了几次相同字符
 int flag=0;
 //判断参数个数是否符合要求
 if(argc!=3){
  printf("Usage: %s "string" filename " , argv[0]);
  return 1;
 }
 //取得要查找的字符长度
 len=strlen(argv[1]);
 if((fd=open(argv[2],O_RDONLY))==-1){
  perror("Cannot open the desired file");
 return 1;
 }
 //不断读取文件内容,然后与给定的字符串比较,如果相同则将flag计数加1
 while(1){
  if(lseek(fd,offset,SEEK_SET)==-1){
   perror("Cannot move the file pointer");
   return 1;
  }
  if(read(fd,buffer,len)<len)
   break;
  buffer[len]='';
  if(strcmp(buffer,argv[1])==0)
   flag++;
  offset++;
 }
 //如果flag大于0,则证明查找到了该字符,输出结果
 if(flag>0)
  printf("Find the string: %s in the file: %s %d times ",argv[1],argv[2],flag);
 if(close(fd)==-1){
  perror("Cannot close the desired file");
  return 1;
 }
 return 0;
}

$ cat test.dat
This is a simple file!
abcdefghigklmnopqrstuvwxyz

$gcc -o test lseek_test.c
$./test "simple" test.dat
Find the string: simple in the file: test.dat 1 times
        之前,由于open函数少写了一对括号,总出现错误:
Cannot move the file pointer: Illegal seek

      

2.6 关闭——close只有一个参数fd,即open返回的文件描述符。调用成功后返回0,或者如果失败,则设置errno变量并返回-1。

#include <unistd.h>

int close(int fd);

       当操作完成以后,就要关闭文件了,只要调用close函数就可以了, 其中fd是要关闭的文件描述符。

       注意,不检查close()的返回值是一个严重的编程错误,原因有二:首先,在网络文件系统中,例如NFS,close调用会因为网络延迟而失败。其次,许多系统都配置有写后缓冲(write-behind caching)的作用,这意味着既使write调用成功返回,操作系统也要等到一个更方便的时候执行实际的磁盘写入操作。正如close(2)手册上面所叙述的:“错误状态可能会在写入操作结束后的晚些时候才报告,但肯定会在关闭文件时报告。在关闭文件时不检查返回值可能导致在不知情的情况下丢失数据。”

       例程:编写一个程序,在当前目录下创建用户可读写的文件“hello.txt”,在其中写入"Hello World!",关闭该文件。再次打开该文件,读取其中的内容并输出在屏幕上。

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>   //包含O_CREAT等定义

#include <stdio.h>

#include <stdlib.h>     //包含exit()

#include <string.h> //add by fantity include strlen

 

#define LENGTH 50

 

int main()

{

        int fd,len,size;

        char *buf="Hello! I'm writing to this file!";

        char str[LENGTH];

        len=strlen(buf);

        /*首先调用open函数创建文件,并指定相应的权限*/

        if((fd=open("hello.txt",O_CREAT | O_RDWR,S_IRUSR | S_IWUSR,0666))<0)

        {  

                perror("open:");

                exit(1);

        }  

        else

        {  

                printf("open file: hello.txt %d ",fd);

        }  

 

        if((size=write(fd,buf,len))<0)

        {  

                perror("write:");

                exit(1);

        }  

        else

        {  

                printf("write:%s ",buf);

        }

        if(close(fd)<0)

        {

                perror("close:");

                exit(1);

        }

        else

        {

                printf("close file:hello.txt ");

        }

 

        if((fd=open("hello.txt",O_RDWR))<0)

        {

                perror("open:");

                exit(1);

        }

        else

        {

                printf("open file:hello.txt again %d ",fd);

        }

 

        /*调用lseek函数,将文件指针调到文件起始,并读取其中的10个字符*/

        lseek(fd,0,SEEK_SET);

        if(read(fd,str,LENGTH)<0)

        {

                perror("read: ");

                exit(1);

        }

        else

        {

                str[len]='';

                printf("read from file:%s ",str);

        }

        if(close(fd)<0)

        {

                perror("close:");

                exit(1);

        }

        else

        {

                printf("close file:hello.txt,again! ");

        }

        exit(0);

        //return 0;

}

 

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>   //包含O_CREAT等定义

#include <stdio.h>

#include <string.h> //add by fantity include strlen

#define LENGTH 20

int main()

{

        int fd,len;

        char str[LENGTH];

 

        fd=open("hello.txt",O_CREAT | O_RDWR,S_IRUSR | S_IWUSR);

 

        if(fd)

        {

                write(fd,"Hello World!",strlen("Hello World!"));

                close(fd);

 

        }

 

        fd=open("hello.txt",O_RDWR);

        len=read(fd,str,LENGTH);

        str[len]='';

        printf("%s ",str);

        close(fd);

        return 0;

}

原文地址:https://www.cnblogs.com/jdfemqi/p/3471036.html