如何编写C程序递归打印指定目录的文件及子目录树结构?
Hey Antonio, let's build this directory tree printer in C together. I've implemented similar tools before, so I'll break down every part so you understand how it works, not just copy-paste code.
Required Headers
First, we need these headers to access system calls for directory handling, file status checks, and string operations:
#include <stdio.h> #include <stdlib.h> #include <dirent.h> #include <sys/stat.h> #include <string.h> #include <errno.h> #include <limits.h> // For PATH_MAX
Step 1: Validate Command-Line Input
Our program needs exactly one argument: the target directory. Let's start by making sure the user provides it correctly:
void print_tree(const char *path, int depth, int *is_last); int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <directory_path>\n", argv[0]); return EXIT_FAILURE; } // Print the root directory first to kick off the tree printf("%s\n", argv[1]); print_tree(argv[1], 0, NULL); return EXIT_SUCCESS; }
Step 2: The Recursive Tree Printing Function
This is the heart of the program. It handles opening directories, filtering out special entries, printing formatted lines, and recursing into subdirectories. The is_last array tracks which parent entries were the last in their directory—this lets us draw the correct vertical lines for the tree structure.
Here's the full function with comments explaining each part:
void print_tree(const char *path, int depth, int *is_last) { // Open the target directory DIR *dir = opendir(path); if (!dir) { fprintf(stderr, "Failed to open directory %s: %s\n", path, strerror(errno)); return; } struct dirent *entry; int entry_count = 0; // First pass: count valid entries (skip . and .. to avoid infinite recursion) while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { entry_count++; } } rewinddir(dir); // Reset directory pointer to start processing entries // Allocate array to track if entries are the last in their directory int *new_is_last = malloc((depth + 1) * sizeof(int)); if (!new_is_last) { fprintf(stderr, "Memory allocation error: %s\n", strerror(errno)); closedir(dir); return; } // Copy parent's is_last status if we're not at the root level if (depth > 0 && is_last != NULL) { memcpy(new_is_last, is_last, depth * sizeof(int)); } int current_entry = 0; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } current_entry++; int is_last_entry = (current_entry == entry_count); new_is_last[depth] = is_last_entry; // Print indentation and vertical lines for tree structure for (int i = 0; i < depth; i++) { printf(is_last[i] ? " " : "│ "); } // Print entry marker (└── for last entries, ├── for others) printf(is_last_entry ? "└── " : "├── "); printf("%s\n", entry->d_name); // Build full path to the child entry char child_path[PATH_MAX]; int snprintf_result = snprintf(child_path, sizeof(child_path), "%s/%s", path, entry->d_name); if (snprintf_result >= sizeof(child_path) || snprintf_result < 0) { fprintf(stderr, "Path too long or invalid for %s\n", entry->d_name); continue; } // Check if the entry is a directory to trigger recursion struct stat stat_buf; if (stat(child_path, &stat_buf) == -1) { fprintf(stderr, "Failed to get info for %s: %s\n", child_path, strerror(errno)); continue; } if (S_ISDIR(stat_buf.st_mode)) { print_tree(child_path, depth + 1, new_is_last); } } // Clean up allocated memory and close the directory free(new_is_last); closedir(dir); }
Step 3: Understanding the Tree Formatting
The trickiest part is getting the vertical lines right. Here's how it works:
- The
is_lastarray tracks, for each depth level, whether that directory entry was the last one in its parent. - For indentation: if a parent entry wasn't the last, we print
│to keep the vertical line going. If it was the last, we print(spaces) to break the line. - For entry markers:
├──for non-last entries,└──for the final entry in a directory.
How to Compile & Test
- Save the code as
tree.c - Compile with GCC:
gcc tree.c -o tree - Run it on a directory:
./tree /path/to/your/directory
You'll get output like this:
./test_dir ├── docs │ ├── README.md │ └── notes.txt ├── src │ ├── main.c │ └── utils.c └── Makefile
Notes & Troubleshooting
- PATH_MAX: This is a system-defined maximum path length. If your system doesn't define it, replace it with a reasonable value like
1024. - Permission Errors: If the program can't open a directory, it'll print an error but continue processing other entries.
- Memory Leaks: We use
mallocfor thenew_is_lastarray and free it at the end of the function to avoid leaks. - Cross-Platform: This uses POSIX functions (
opendir,readdir,stat), so it works on Linux, macOS, and other Unix-like systems. For Windows, you'd need to use Win32 API functions instead.
内容的提问来源于stack exchange,提问作者Antonio

