You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言实现tree命令:匹配ASCII格式标准输出的问题求助

问题:实现与tree命令完全一致的ASCII树形输出

我留意到社区里已经有不少关于树形目录打印的讨论,但我的需求更具体——要完全匹配LC_ALL=C tree -n --charset=ascii [options] [dirs]的输出格式。目前我写的C代码已经能完成基本的目录遍历和文件/目录计数,但层级间的管道符始终无法正确显示,想请教调整思路。

现有代码(省略选项处理与符号链接检查)

#include <ctype.h> 
#include <dirent.h> 
#include <fnmatch.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/stat.h> 
#include <unistd.h> 

//Functions prototypes
void printaTree(int, char *, int *, int *, int);
void printTreeconPattern(int livello, char * pattern);

//MAIN
int main(int argc, char * argv[]) {
    int checkgetopt;
    char * pattern;
    char * optargvalue;
    int maxHeight;
    int checkHidden = 0;
    int indice;
    int dirsindex = 0;
    char ** directories;
    directories = malloc(argc * sizeof(char *));
    for (indice = optind; indice < argc; indice++) {
        directories[dirsindex] = malloc(strlen(argv[indice]) * sizeof(char));
        strcpy(directories[dirsindex], argv[indice]);
        dirsindex++;
    }
    int livello = 0;
    int i;
    int countfiles = 0;
    int countDirs = 0;
    if (dirsindex == 0) {
        printf("%s\n", ".");
        printaTree(livello, ".", &countfiles, &countDirs, checkHidden);
    } else {
        for (i = 0; i < dirsindex; i++) {
            printf("%s\n", directories[i]);
            printaTree(livello, directories[i], &countfiles, &countDirs, checkHidden);
        }
    }
    printf("\n%d directories, %d files\n", countDirs, countfiles);
    free(directories);
    return 0;
}

void printaTree(int livello, char * directory, int * filenum, int * dirnum, int hidden) {
    struct dirent ** dobjects;
    int numeroOggetti = scandir(directory, &dobjects, NULL, alphasort);
    if (numeroOggetti == -1) {
        perror("scandir");
    }
    char pathnuovo[1024];
    int i;
    int checkSym = 0;
    char * buf;
    for (i = 0; i < numeroOggetti; i++) {
        if ((strcmp(dobjects[i]->d_name, ".") == 0) || (strcmp(dobjects[i]->d_name, "..") == 0))
            continue;
        if (hidden == 0) {
            if (dobjects[i]->d_name[0] == '.') {
                continue;
            }
        }
        //Print how Tree command would print:
        if (i != numeroOggetti - 1) {
            if (livello != 0) {
                printf("%*s", livello * 4 - 1, "");
            } else {
                printf("%*s", livello, "");
            }
            printf("|-- ");
            printf("%s\n", dobjects[i]->d_name);
        } else {
            if (livello != 0) {
                printf("%*s", livello * 4 - 1, "");
            } else {
                printf("%*s", livello, "");
            }
            printf("`-- ");
            printf("%s\n", dobjects[i]->d_name);
        }
        if (dobjects[i]->d_type == DT_REG) {
            (*filenum)++;
        }
        if (dobjects[i]->d_type == DT_DIR) {
            (*dirnum)++;
            livello = livello + 1;
            snprintf(pathnuovo, sizeof pathnuovo, "%s%s%s", directory, "/", dobjects[i]->d_name);
            printaTree(livello, pathnuovo, filenum, dirnum, hidden);
            livello = livello - 1;
        }
    }
}

测试场景

测试目录结构

RootDIRname
file1
file2
dir1
├── file4
├── dir2
│   ├── file6
│   └── file7
└── dir3
    ├── file10
    └── file11
file8
file9

我的代码输出

RootDIRname
|-- file1
|-- file2
|-- dir1
|-- file4
|-- dir2
|-- file6
`-- file7
`-- dir3
|-- file10
`-- file11
|-- file8
`-- file9

标准tree命令的正确输出

RootDIRname
|-- file1
|-- file2
|-- dir1
|   |-- file4
|   |-- dir2
|   |   |-- file6
|   |   `-- file7
|   `-- dir3
|       |-- file10
|       `-- file11
|-- file8
`-- file9

问题核心与思路指导

你现在的核心问题是:递归遍历过程中,没有跟踪当前路径上每一层级是否还有未处理的兄弟节点,所以无法正确输出用于对齐的竖线|。这里给你几个关键调整思路:

  1. 添加层级状态跟踪参数
    给printaTree函数增加一个状态数组(比如int *is_last),数组的第k位用来记录第k层的当前节点是否是该层的最后一个节点:1表示不是最后一个(后续还有兄弟节点),0表示是最后一个。

  2. 动态生成前缀字符串
    在打印每个节点之前,根据状态数组生成前缀:

    • 遍历当前层级之前的所有层级,对于每一层:
      • 如果该层还有后续兄弟节点,输出| (竖线加三个空格);
      • 如果是该层最后一个节点,输出 (四个空格);
    • 最后再加上当前节点的标记:如果当前节点不是所在层的最后一个,输出|-- ;如果是最后一个,输出-- 。
  3. 递归时更新与恢复状态

    • 进入子目录递归前,先更新状态数组:把当前层级的状态设为(i != numeroOggetti - 1)(即是否有后续兄弟节点);
    • 递归完成后,要恢复这个状态(因为其他兄弟节点的子目录不需要这个标记),或者在递归调用时传递状态数组的副本,避免修改父级的状态。

举个例子,当处理dir1下的dir2时,dir1不是根目录的最后节点,所以dir2的前缀开头会有| ,再加上自身的层级标记,就能和上层的dir1形成正确的竖线连接,复现tree命令的格式。

内容的提问来源于stack exchange,提问作者Luca9504

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:42:27