引言 - 业务有点麻烦
C 功能很强大, 同样书写起来会谨慎(拖泥带水). 不妨通过一个小问题来描述裹脚的 C
需求:
用 C 创建一个文件!
难点在于
1. 文件路径切割成 目录 + 文件名
2. 目录 多级创建
3. 跨平台
这个问题也挺适合做线下面试作业.
本文围绕下面几个重点讲述
1. 获取文件更新时间
2. 目录创建和文件删除
3. 配置自动更新
应对平台是 Winds cl 和 Linux gcc :)
在开始之前, 先介绍一些文件辅助基础函数, 构造了两个最初的原始的 mkdir 和 mtime 函数.
mkdir 在 shell 中用于构建目录,常很用. 对于 mtime 是 Linux 对于文件最后修改时间.
最初来自于下面文件详细信息结构中
struct stat {
unsigned long st_dev; /* Device. */
unsigned long st_ino; /* File serial number. */
unsigned int st_mode; /* File mode. */
unsigned int st_nlink; /* Link count. */
unsigned int st_uid; /* User ID of the file's owner. */
unsigned int st_gid; /* Group ID of the file's group. */
unsigned long st_rdev; /* Device number, if device. */
unsigned long __pad1;
long st_size; /* Size of file, in bytes. */
int st_blksize; /* Optimal block size for I/O. */
int __pad2;
long st_blocks; /* Number 512-byte blocks allocated. */
long st_atime; /* Time of last access. */
unsigned long st_atime_nsec;
long st_mtime; /* Time of last modification. */
unsigned long st_mtime_nsec;
long st_ctime; /* Time of last status change. */
unsigned long st_ctime_nsec;
unsigned int __unused4;
unsigned int __unused5;
};
在结构体中 st_atime, st_mtime, st_ctime 字段可以知道, Linux 文件有三个时间属性:
1. mtime: 文件内容最后修改时间
2. ctime: 文件状态改变时间, 如权限, 属性被更改
3. atime: 文件内容被访问时间, 如 cat, less 等
在默认情况下, ls 显示出来的是该文件的 mtime, 即文件内容最后修改时间.
如果你需要查看另外两个时间, 可以使用 ls -l --time ctime 命令.
消化上面小知识, 加上下面先入为主的设计思路, 就构建了代码 head 部分
#ifndef _H_FILE
#define _H_FILE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __GNUC__
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
//
// mkdir - 通用的单层目录创建函数宏, 等价于 mkdir path
// path : 目录路径加名称
// return : 0表示成功, -1表示失败, 失败原因都在 errno
//
#undef mkdir
#define mkdir(path) \
mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
//
// mtime - 得到文件最后修改时间
// path : 文件名称
// return : 返回时间戳, -1 表示错误
//
inline time_t mtime(const char * path) {
struct stat ss;
// 数据最后的修改时间
return stat(path, &ss) ? -1 : ss.st_mtime;
}
#endif
#ifdef _MSC_VER
#include <io.h>
#include <direct.h>
#include <windows.h>
// int access(const char * path, int mode /* 四个检测宏 */);
#ifndef F_OK
# define F_OK (0)
#endif
#ifndef X_OK
# define X_OK (1)
#endif
#ifndef W_OK
# define W_OK (2)
#endif
#ifndef R_OK
# define R_OK (4)
#endif
inline time_t mtime(const char * path) {
WIN32_FILE_ATTRIBUTE_DATA wfad;
if (!GetFileAttributesEx(path, GetFileExInfoStandard, &wfad))
return -1;
// 基于 winds x64 sizeof(long) = 4
return *(time_t *)&wfad.ftLastWriteTime;
}
#endif
#endif
原始的 mkdir 仿照 Winds 上面的 mkdir 接口设计.
_Check_return_ _CRT_NONSTDC_DEPRECATE(_mkdir)
_ACRTIMP int __cdecl mkdir(
_In_z_ char const* _Path
);
最终行为和 Linux 的 mkdir 创建目录命令一致(0665,即-rw-rw-r-x).
mtime 得到文件最后修改时间. 主要用于获取时间变化促使配置自动更新.
F_OK, X_OK, W_OK, R_OK 在 Winds 上实现 access 接口缺少的宏定义.
更加具体可以参照
file.h
前言 - 文件辅助操作
前言部分可能最精华, 开始步入正轨 ~
//
// mkdirs - 创建多级目录
// path : 目录路径
// return : < 0 is error, 0 is success
//
int
mkdirs(const char * path) {
char c, * p, * s;
// 参数错误直接返回
if (!path || !*path) return -2;
// 文件存在 or 文件一次创建成功 直接返回
if (!access(path, F_OK) || !mkdir(path))
return 0;
// 跳过第一个 ['/'|'\\'] 检查是否是多级目录
p = (char *)path;
while ((c = *++p) != '\0')
if (c == '/' || c == '\\')
break;
if (c == '\0') return -1;
// 开始循环构建多级目录
s = p = strdup(path);
while ((c = *++p) != '\0') {
if (c == '/' || c == '\\') {
*