创建递归目录树
21 Jul 2016昨天 Zorro 在鼓捣一个C程序,看见我问怎么用递归方法创建一个递归的目录树结构: 指定子目录数量和层数。递归太烧脑,想了想可以用循环实现,很久没写C了,写写练习一下:
create_dirtree.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
uint64_t power_uint(uint64_t base, int x)
{
uint64_t sum = 1;
for (int i=0; i<x; i++) {
sum = sum * base;
}
return sum;
}
/*
* do the action like "mkdir(1) -p"
*/
int mkdir_p(char * path)
{
char * ptr = NULL;
int i, len, ret;
len = strlen(path);
if (NULL == (ptr = malloc(len + 2))) {
return (-1);
}
strcpy(ptr, path);
/*
* convert "a/b/c/d" to "a/b/c/d/"
*/
if (ptr[len - 1] != '/') {
ptr[len] = '/'; ptr[len + 1] = '\0';
}
/*
* create dir recursively
*/
for (i = 1; i < len + 1; i++) {
if (ptr[i] == '/') {
ptr[i] = '\0';
ret = mkdir(ptr, 0755);
if (ret < 0 && errno != EEXIST) {
free(ptr);
return (-1);
}
ptr[i] = '/';
}
}
free(ptr);
return (0);
}
int main(int argc, char *argv[])
{
uint64_t base = (uint64_t)atoll(argv[1]);
int x = atoi(argv[2]);
uint64_t leafn = power_uint(base, x);
printf("%lu ^ %u = %lu\n", base, x, leafn);
char path[PATH_MAX] = {0};
uint64_t *binpath = calloc(sizeof(uint64_t), (size_t)x);
for (uint64_t i = 0; i < leafn; i++) {
path[0] = '\0';
uint64_t S = i;
for (int j = 0; j < x; j++) {
binpath[j] = S % base;
S = S / base;
if (S == 0) {
break;
}
}
for (int k = x-1; k >= 0; k--) {
sprintf(&path[0]+(strlen(path)), "%lu/", binpath[k]);
}
mkdir_p(path);
}
return 0;
}
因为偷懒实现了一个 mkdir_p(),问题被简化了,但是这样会有很多冗余的 mkdir(2) 调用,如果节点数量很多的话浪费性能。所以给 mkdir_p() 增加第二个参数,避免无用的mkdir(2)调用:
create_dirtree2.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
uint64_t power_uint(uint64_t base, int x)
{
uint64_t sum = 1;
int i = 0;
for (i=0; i<x; i++) {
sum = sum * base;
}
return sum;
}
/*
* do the action like "mkdir(1) -p"
*/
int mkdir_p(char * path, int skip)
{
int i, len, ret, depth = 0;
int syscalln = 0;
char ptr[PATH_MAX] = {0};
len = strlen(path);
strcpy(ptr, path);
/*
* convert "a/b/c/d" to "a/b/c/d/"
*/
if (ptr[len - 1] != '/') {
ptr[len] = '/'; ptr[len + 1] = '\0';
}
/*
* create dir recursively
*/
depth = skip;
for (i = 1; i < len; i++) {
if (ptr[i] == '/' && depth-- <= 0) {
ptr[i] = '\0';
ret = mkdir(ptr, 0755);
++syscalln;
if (ret < 0) {
if (errno != EEXIST) {
fprintf(stderr, "[Err] mkdir(%s in %s) %d, %d, %s\n",
ptr, path, skip, depth, strerror(errno));
return (syscalln);
} else {
fprintf(stderr, "[War] mkdir(%s in %s) %d, %d, %s\n",
ptr, path, skip, depth, strerror(errno));
}
}
ptr[i] = '/';
}
}
return (syscalln);
}
int main(int argc, char *argv[])
{
uint64_t base = (uint64_t)atoll(argv[1]);
int x = atoi(argv[2]);
int syscalln = 0;
uint64_t leafn = power_uint(base, x);
printf("%lu ^ %u = %lu\n", base, x, leafn);
char path[PATH_MAX] = {0};
uint64_t *binpath = calloc(sizeof(uint64_t), (size_t)x);
uint64_t i = 0;
for (i = 0; i < leafn; i++) {
path[0] = '\0';
uint64_t S = i;
int j = 0;
int skip = 0;
skip = x - 1;
for (j = 0; j < x; j++) {
binpath[j] = S % base;
S = S / base;
if (S == 0) {
break;
}
}
/* generate the path */
int k = 0;
for (k = x-1; k >= 0; k--) {
sprintf(&path[0]+(strlen(path)), "%lu/", binpath[k]);
}
/* fix skip level */
int n = 0;
for (n=0; n<x-1; n++) {
if (binpath[n] != 0)
break;
--skip;
}
if (skip != (x-1)) {
syscalln += mkdir_p(path, skip);
} else {
int ret;
ret = mkdir(path, 0755);
++syscalln;
if (ret < 0) {
fprintf(stderr, "[Err] mkdir(%s) %d, %s\n",
path, skip, strerror(errno));
}
}
}
printf("mkdir syscalln = %d\n", syscalln);
return 0;
}