镜缘浮影 小人本住在 苏州的城外 家里有屋又有田 生活乐无边

文件目录 (一).stat

2017-01-04
wilmosfang
c
原文地址 http://soft.dog/2017/01/04/c-filestat-01/

前言

UNIX/Linux 的缔造者们将数据的 来源和目标 都抽象为 文件,所以在 UNIX/Linux 系统中 一切皆文件

一切皆文件 不仅仅对磁盘,还包括鼠标,键盘,显示器这些设备

那么目录算不算文件呢?当然算,目录是一种特殊的文件,目录里存放的内容是子目录和文件的索引信息

这里分享一下我在学习文件目录过程中的笔记和心得


概要


代码示例

要求

统计 /home/emacs/c 下有几个C语言源文件 .c ,并找出文件大小最大的那个文件名

Tip: 要求掌握opendir,readdir,closedir,rewinddir用法

代码示例

#include <stdio.h>
#include <dirent.h> //DIR,opendir,readdir,rewinddir,closedir 的定义和声明都在这个头文件里面
#include <string.h> 
#include <sys/stat.h> // stat 结构体的定义在里面
#define MAX 1000

int main()
{
  char *dirpath="/home/emacs/c"; 
  DIR *dir=NULL; 
  struct dirent *de=NULL;
  struct stat fs;
  int len=0,i=0;
  unsigned long maxsize=0;
  char filename[MAX]; //各种变量的声明与初始化

  if(NULL == (dir=opendir(dirpath))) //打开目录文件并且将目录指针赋给dir,出错则提示并且返回
  {
    printf("open dir error:%s\n",dirpath);
    return -1;
  }
  
  while(NULL != (de=readdir(dir))) //遍历目录中的每个条目
  {
    len=strlen(de->d_name); //获取一个条目的文件名长度存入到len中
    if(de->d_name[len-1]=='c' && de->d_name[len-2]=='.' )  //如果一个条目是以'.c'结尾的
    {
      i++; //使用i进行计数
      strcpy(filename,dirpath);
      strcat(filename,"/");
      strcat(filename,de->d_name); //拼接成一个完整的绝对路径
      stat(filename,&fs);  //将这个文件的属性信息存入到fs结构体中
      if(maxsize < fs.st_size)maxsize=fs.st_size;  //总是将最大的文件大小值存入到maxsize变量中
    }
  }
  printf("the total number of .c file is :%d \nthe max size is:%ld\nthe max size .c file are :\n",i,maxsize); //打印出'.c'文件个数,和最大的文件大小
  rewinddir(dir); //重新定位目录指针到目录开始处
  while(NULL != (de=readdir(dir)))  //遍历目录中的每个条目
  {
    len=strlen(de->d_name); //获取一个条目的文件名长度存入到len中
    if(de->d_name[len-1]=='c' && de->d_name[len-2]=='.' )  //如果一个条目是以'.c'结尾的
    {
      strcpy(filename,dirpath);
      strcat(filename,"/");
      strcat(filename,de->d_name); //拼接成一个完整的绝对路径
      stat(filename,&fs); //将这个文件的属性信息存入到fs结构体中
      if(fs.st_size == maxsize)printf("%s\n",filename);  //将大小为maxsize的条目进行打印
    }
  }
  
  closedir(dir); //关闭目录
  return 0;
}

编译执行

emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc filestat.x filestat.c
emacs@ubuntu:~/c$ ./filestat.x 
the total number of .c file is :48 
the max size is:5686
the max size .c file are :
/home/emacs/c/sqlite.c
emacs@ubuntu:~/c$
emacs@ubuntu:~/c$ ll -trl *.c | sort -nk 5  | tail -n 5
-rw-r--r-- 1 emacs emacs 5250 2016-12-29 08:23 ftpclient.c
-rw-r--r-- 1 emacs emacs 5352 2016-12-14 01:04 6d.c
-rw-r--r-- 1 emacs emacs 5352 2016-12-14 01:06 toblog.c
-rw-r--r-- 1 emacs emacs 5407 2016-12-29 06:44 ftpserver.c
-rw-r--r-- 1 emacs emacs 5686 2016-12-29 01:39 sqlite.c
emacs@ubuntu:~/c$ 

编译执行过程中没有报错,从结果来看,符合预期


DIR

代码中有一个这样的定义

DIR *dir=NULL;

DIR 是一种新的结构体

emacs@ubuntu:/usr/include$ grep  DIR dirent.h  | grep typedef
typedef struct __dirstream DIR;
emacs@ubuntu:/usr/include$ grep dirstream  *  -r
dirent.h:typedef struct __dirstream DIR;
emacs@ubuntu:/usr/include$ 

可知 DIR__dirstream 的别名,但是头文件中没有有关于 __dirstream 的详细定义

通过查资料,得知 __dirstream 有如下定义

struct __dirstream   
   {   
    void *__fd;   //`struct hurd_fd' pointer for descriptor.
    char *__data;    //Directory block.
    int __entry_data;   //Entry number `__data' corresponds to. 
    char *__ptr;  //Current pointer into the block.  
    int __entry_ptr;  //Entry number `__ptr' corresponds to.  
    size_t __allocation;   //Space allocated for the block. 
    size_t __size;    //Total valid data in the block.
    __libc_lock_define (, __lock)  //Mutex lock for this structure.  
   };

总体看来,就是关于一个目录的描述信息

常用的目录操作函数

dirent.h 中对常用的目录操作函数作了声明

/* Open a directory stream on NAME.
   Return a DIR stream on the directory, or NULL if it could not be opened.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern DIR *opendir (__const char *__name) __nonnull ((1));
/* Close the directory stream DIRP.
   Return 0 if successful, -1 if not.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern int closedir (DIR *__dirp) __nonnull ((1));
/* Read a directory entry from DIRP.  Return a pointer to a `struct
   dirent' describing the entry, or NULL for EOF or error.  The
   storage returned may be overwritten by a later readdir call on the
   same DIR stream.

   If the Large File Support API is selected we have to use the
   appropriate interface.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
#ifndef __USE_FILE_OFFSET64
extern struct dirent *readdir (DIR *__dirp) __nonnull ((1));
#else
/* Rewind DIRP to the beginning of the directory.  */
extern void rewinddir (DIR *__dirp) __THROW __nonnull ((1));
  • opendir
  • closedir
  • readdir
  • rewinddir

除了 opendir 接收一个文件名字符串,返回一个 DIR 型指针,其它几个函数的参数都是接受一个 DIR 型的指针


dirent

代码中有一个这样的定义

struct dirent *de=NULL;

dirent 是一种新的结构体

在头文件中有所定义

emacs@ubuntu:/usr/include$ grep dirent dirent.h  | head -n 3
 *	POSIX Standard: 5.1.2 Directory Operations	<dirent.h>
/* This file defines `struct dirent'.
#include <bits/dirent.h>
emacs@ubuntu:/usr/include$ 

从中可知是定义到了 bits/dirent.h

#ifndef _DIRENT_H
# error "Never use <bits/dirent.h> directly; include <dirent.h> instead."
#endif

struct dirent
  {
#ifndef __USE_FILE_OFFSET64
    __ino_t d_ino; //索引节点号, inode number
    __off_t d_off; //在目录文件中的偏移,offset to this dirent  
#else
    __ino64_t d_ino; 
    __off64_t d_off;
#endif
    unsigned short int d_reclen;  
    unsigned char d_type; //文件类型, the type of d_name
    char d_name[256];           /* We must not include limits.h! */ //文件名,最长255字符 file name (null-terminated) 
  };

其中提供的信息也比较少,但是从中我们可以看出,文件名最大长度只能为255的原因 ,因为系统只给了256个字符的长度用来存储文件名

总体来看,这个目录条目结构体,也是一个简略的索引


stat

代码中有一个这样的定义

struct stat fs;

stat 是一种新的结构体

在头文件中有所定义

emacs@ubuntu:/usr/include$ grep stat sys/stat.h | grep include
#include <bits/stat.h>
emacs@ubuntu:/usr/include$ 

详细信息在 bits/stat.h

#ifndef _SYS_STAT_H
# error "Never include <bits/stat.h> directly; use <sys/stat.h> instead."
#endif


struct stat
  {
    __dev_t st_dev;		/* Device.  */  //文件使用的设备号
#if __WORDSIZE == 32
    unsigned short int __pad1;
#endif
#if __WORDSIZE == 64 || !defined __USE_FILE_OFFSET64
    __ino_t st_ino;		/* File serial number.	*/  //索引节点号
#else
    __ino_t __st_ino;			/* 32bit file serial number.	*/
#endif  //索引节点号
#else
#if __WORDSIZE == 32
    __mode_t st_mode;			/* File mode.  */  //文件访问权限  
    __nlink_t st_nlink;			/* Link count.  */  //文件的硬连接数
#else
    __nlink_t st_nlink;		/* Link count.  */  //文件的硬连接数
    __mode_t st_mode;		/* File mode.  */  //文件访问权限
#endif
    __uid_t st_uid;		/* User ID of the file's owner.	*/  //所有者用户识别号
    __gid_t st_gid;		/* Group ID of the file's group.*/  //组识别号
#if __WORDSIZE == 64
    int __pad0;
#endif
    __dev_t st_rdev;		/* Device number, if device.  */
#if __WORDSIZE == 32
    unsigned short int __pad2;
#endif
#if __WORDSIZE == 64 || !defined __USE_FILE_OFFSET64
    __off_t st_size;			/* Size of file, in bytes.  */  //以字节为单位的文件容量   
#else
    __off64_t st_size;			/* Size of file, in bytes.  */  //以字节为单位的文件容量   
#endif
    __blksize_t st_blksize;	/* Optimal block size for I/O.  */
#if __WORDSIZE == 64 || !defined __USE_FILE_OFFSET64
    __blkcnt_t st_blocks;		/* Number 512-byte blocks allocated. */ //该文件所占的磁盘块   
#else
    __blkcnt64_t st_blocks;		/* Number 512-byte blocks allocated. */
#endif
#ifdef __USE_MISC
    /* Nanosecond resolution timestamps are stored in a format
       equivalent to 'struct timespec'.  This is the type used
       whenever possible but the Unix namespace rules do not allow the
       identifier 'timespec' to appear in the <sys/stat.h> header.
       Therefore we have to handle the use of this header in strictly
       standard-compliant sources special.  */
    struct timespec st_atim;		/* Time of last access.  */  //最后一次访问该文件的时间
    struct timespec st_mtim;		/* Time of last modification.  */  //最后一次修改该文件的时间 
    struct timespec st_ctim;		/* Time of last status change.  */   //最后一次改变该文件状态的时间  
# define st_atime st_atim.tv_sec	/* Backward compatibility.  */
# define st_mtime st_mtim.tv_sec
# define st_ctime st_ctim.tv_sec
#else
    __time_t st_atime;			/* Time of last access.  */
    unsigned long int st_atimensec;	/* Nscecs of last access.  */
    __time_t st_mtime;			/* Time of last modification.  */
    unsigned long int st_mtimensec;	/* Nsecs of last modification.  */
    __time_t st_ctime;			/* Time of last status change.  */
    unsigned long int st_ctimensec;	/* Nsecs of last status change.  */
#endif
#if __WORDSIZE == 64
    long int __unused[3];
#else
# ifndef __USE_FILE_OFFSET64
    unsigned long int __unused4;
    unsigned long int __unused5;
# else
    __ino64_t st_ino;			/* File serial number.	*/
# endif
#endif
  };

这里可以获得文件的绝大部分属性信息

Tip: 头文件里有很多如下形式的条件编译,是为了进行兼容处理

#ifndef
#else
#endif

读取文件属性过程

  • 使用 opendir 函数打开一个目录文件(文件名字符串),返回一个 DIR 指针
  • 使用 readdir 函数读取 DIR 指针,返回一个 dirent 指针
  • 通过 stat 函数读取 direntP->d_name (文件名字符串),得到 stat 结构体
  • 通过 stat 结构体,获取详细属性信息

有点小复杂


总结

以下这些函数可以应对绝大部分的文件或目录属性查询需求

  • opendir
  • closedir
  • readdir
  • rewinddir
  • stat

通过各方面资料弄懂其参数的意义和返回值的类型,是熟练掌握的基础

原文地址 http://soft.dog/2017/01/04/c-filestat-01/

评论