15. truncate - 截断文件 見出しへのリンク

函数介绍 見出しへのリンク

truncate系统调用用于将文件截断到指定长度。如果指定长度小于文件当前长度,则文件末尾被截断;如果指定长度大于文件当前长度,则文件被扩展,扩展部分填充为零。

函数原型 見出しへのリンク

#include <unistd.h>
#include <sys/types.h>

int truncate(const char *path, off_t length);

功能 見出しへのリンク

根据文件路径将文件截断或扩展到指定长度。

参数 見出しへのリンク

  • const char *path: 文件路径名
  • off_t length: 目标文件长度(字节)

返回值 見出しへのリンク

  • 成功时返回0
  • 失败时返回-1,并设置errno:
    • EACCES: 权限不足
    • EFBIG: 长度参数过大
    • EIO: I/O错误
    • EISDIR: 路径指向目录
    • ELOOP: 符号链接循环
    • ENAMETOOLONG: 路径名过长
    • ENOENT: 文件不存在
    • ENOTDIR: 路径前缀不是目录
    • EPERM: 操作不被允许
    • EROFS: 文件在只读文件系统上
    • ETXTBSY: 文件是正在执行的程序

相似函数 見出しへのリンク

  • ftruncate(): 通过文件描述符截断文件
  • open()配合O_TRUNC标志

示例代码 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int main() {
    int fd;
    struct stat stat_buf;
    
    printf("=== Truncate函数示例 ===\n");
    
    // 示例1: 基本的文件截断操作
    printf("\n示例1: 基本的文件截断操作\n");
    
    // 创建测试文件并写入数据
    fd = open("test_truncate.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
    if (fd == -1) {
        perror("创建测试文件失败");
        exit(EXIT_FAILURE);
    }
    
    // 写入较长的数据
    const char *original_data = 
        "This is a long piece of text that will be used to demonstrate "
        "the truncate function. It contains multiple sentences and is "
        "quite lengthy to show how truncation works.";
    
    if (write(fd, original_data, strlen(original_data)) == -1) {
        perror("写入原始数据失败");
        close(fd);
        exit(EXIT_FAILURE);
    }
    
    printf("写入原始数据,长度: %ld 字节\n", (long)strlen(original_data));
    close(fd);
    
    // 获取原始文件状态
    if (stat("test_truncate.txt", &stat_buf) == -1) {
        perror("获取文件状态失败");
    } else {
        printf("原始文件大小: %ld 字节\n", stat_buf.st_size);
    }
    
    // 使用truncate截断文件到50字节
    printf("\n使用truncate将文件截断到50字节...\n");
    if (truncate("test_truncate.txt", 50) == -1) {
        perror("截断文件失败");
    } else {
        printf("文件截断成功\n");
    }
    
    // 检查截断后的文件大小
    if (stat("test_truncate.txt", &stat_buf) == -1) {
        perror("获取截断后文件状态失败");
    } else {
        printf("截断后文件大小: %ld 字节\n", stat_buf.st_size);
    }
    
    // 读取截断后的数据验证
    fd = open("test_truncate.txt", O_RDONLY);
    if (fd != -1) {
        char buffer[100];
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0';
            printf("截断后文件内容: %s\n", buffer);
        }
        close(fd);
    }
    
    // 示例2: 扩展文件
    printf("\n示例2: 扩展文件\n");
    
    // 使用truncate将文件扩展到100字节
    printf("使用truncate将文件扩展到100字节...\n");
    if (truncate("test_truncate.txt", 100) == -1) {
        perror("扩展文件失败");
    } else {
        printf("文件扩展成功\n");
    }
    
    // 检查扩展后的文件大小
    if (stat("test_truncate.txt", &stat_buf) == -1) {
        perror("获取扩展后文件状态失败");
    } else {
        printf("扩展后文件大小: %ld 字节\n", stat_buf.st_size);
    }
    
    // 读取扩展后的数据
    fd = open("test_truncate.txt", O_RDONLY);
    if (fd != -1) {
        char buffer[150];
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
        if (bytes_read > 0) {
            printf("扩展后文件前%d字节内容:\n", (int)bytes_read);
            // 打印可见字符
            for (int i = 0; i < bytes_read && i < 60; i++) {
                if (buffer[i] >= 32 && buffer[i] <= 126) {
                    putchar(buffer[i]);
                } else {
                    printf("[%02x]", (unsigned char)buffer[i]);
                }
            }
            printf("\n");
        }
        close(fd);
    }
    
    // 示例3: 截断到0字节(清空文件)
    printf("\n示例3: 截断到0字节(清空文件)\n");
    
    printf("将文件截断到0字节...\n");
    if (truncate("test_truncate.txt", 0) == -1) {
        perror("清空文件失败");
    } else {
        printf("文件清空成功\n");
    }
    
    if (stat("test_truncate.txt", &stat_buf) == -1) {
        perror("获取清空后文件状态失败");
    } else {
        printf("清空后文件大小: %ld 字节\n", stat_buf.st_size);
    }
    
    // 示例4: 不同长度的截断演示
    printf("\n示例4: 不同长度的截断演示\n");
    
    // 重新创建文件并写入数据
    fd = open("test_truncate.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
    if (fd != -1) {
        const char *test_data = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        if (write(fd, test_data, strlen(test_data)) != -1) {
            printf("重新写入测试数据: %s\n", test_data);
        }
        close(fd);
    }
    
    // 演示不同长度的截断
    off_t lengths[] = {5, 15, 30, 50};
    for (int i = 0; i < 4; i++) {
        printf("截断到%ld字节: ", lengths[i]);
        if (truncate("test_truncate.txt", lengths[i]) == -1) {
            printf("失败 - %s\n", strerror(errno));
        } else {
            if (stat("test_truncate.txt", &stat_buf) == -1) {
                printf("获取状态失败\n");
            } else {
                printf("成功,新大小: %ld字节\n", stat_buf.st_size);
                
                // 读取并显示内容
                fd = open("test_truncate.txt", O_RDONLY);
                if (fd != -1) {
                    char buffer[100];
                    ssize_t bytes_read = read(fd, buffer, stat_buf.st_size);
                    if (bytes_read > 0) {
                        buffer[bytes_read] = '\0';
                        printf("  内容: %s\n", buffer);
                    }
                    close(fd);
                }
            }
        }
    }
    
    // 示例5: 错误处理演示
    printf("\n示例5: 错误处理演示\n");
    
    // 尝试截断不存在的文件
    if (truncate("nonexistent_file.txt", 100) == -1) {
        printf("截断不存在的文件: %s\n", strerror(errno));
    }
    
    // 尝试对只读文件系统上的文件操作
    // 这个测试可能需要特殊环境,我们只演示概念
    printf("对只读文件系统操作会返回EROFS错误\n");
    
    // 尝试使用负数长度
    if (truncate("test_truncate.txt", -10) == -1) {
        printf("使用负数长度: %s\n", strerror(errno));
    }
    
    // 尝试对目录操作
    if (truncate(".", 100) == -1) {
        printf("对目录截断: %s\n", strerror(errno));
    }
    
    // 示例6: 实际应用场景
    printf("\n示例6: 实际应用场景\n");
    
    // 场景1: 日志文件轮转
    printf("场景1: 日志文件轮转\n");
    int log_fd = open("application.log", O_CREAT | O_RDWR | O_APPEND, 0644);
    if (log_fd != -1) {
        // 模拟写入大量日志
        for (int i = 0; i < 100; i++) {
            char log_entry[100];
            sprintf(log_entry, "Log entry %d: Application is running normally\n", i);
            write(log_fd, log_entry, strlen(log_entry));
        }
        close(log_fd);
        
        // 检查日志文件大小
        if (stat("application.log", &stat_buf) == 0) {
            printf("日志文件大小: %ld 字节\n", stat_buf.st_size);
            
            // 如果文件太大,截断到1KB
            if (stat_buf.st_size > 1024) {
                printf("日志文件过大,截断到1KB...\n");
                if (truncate("application.log", 1024) == -1) {
                    perror("日志文件截断失败");
                } else {
                    printf("日志文件截断成功\n");
                }
            }
        }
        
        unlink("application.log");
    }
    
    // 场景2: 临时文件大小控制
    printf("场景2: 临时文件大小控制\n");
    int temp_fd = open("temp_file.dat", O_CREAT | O_RDWR, 0644);
    if (temp_fd != -1) {
        // 创建一个大文件
        char large_data[1000];
        memset(large_data, 'X', sizeof(large_data));
        for (int i = 0; i < 10; i++) {
            write(temp_fd, large_data, sizeof(large_data));
        }
        close(temp_fd);
        
        if (stat("temp_file.dat", &stat_buf) == 0) {
            printf("临时文件原始大小: %ld 字节\n", stat_buf.st_size);
            
            // 将文件大小调整为5KB
            printf("调整临时文件大小到5KB...\n");
            if (truncate("temp_file.dat", 5120) == -1) {
                perror("调整文件大小失败");
            } else {
                printf("文件大小调整成功\n");
                
                if (stat("temp_file.dat", &stat_buf) == 0) {
                    printf("调整后文件大小: %ld 字节\n", stat_buf.st_size);
                }
            }
        }
        
        unlink("temp_file.dat");
    }
    
    // 清理资源
    printf("\n清理资源...\n");
    if (unlink("test_truncate.txt") == -1) {
        perror("删除测试文件失败");
    } else {
        printf("成功删除测试文件\n");
    }
    
    return 0;
}