C语言文件大小获取:fseek/ftell与stat方法详解与实践
在实际 C 语言项目中处理文件时经常需要知道文件的大小比如读取整个文件到内存、校验文件完整性或者限制上传文件体积。但 C 语言标准库并没有直接提供一个类似get_file_size()的函数这让很多初学者在第一次需要获取文件大小时感到困惑。本文将围绕 C 语言中几种获取文件大小的常用方法从基础概念到实际代码实现逐步讲解其原理、适用场景和注意事项。无论你是刚接触文件操作的初学者还是需要在嵌入式或系统编程中处理文件的开发者都能通过本文掌握可靠的文件大小获取方案。1. 理解文件大小与 C 语言文件操作基础1.1 文件大小在操作系统中的表示方式在大多数操作系统中文件大小通常以字节为单位存储为无符号整数。这个信息保存在文件的元数据metadata中而不是文件内容本身。当我们谈论“获取文件大小”时实际上是在读取文件系统的元数据信息。不同系统对文件大小的表示有所差异在 Linux/Unix 系统中使用stat结构体存储文件信息在 Windows 系统中可以使用_stat或文件句柄操作32 位系统通常最大支持 2GB 文件64 位系统支持更大的文件1.2 C 语言文件操作的核心函数C 语言通过标准库stdio.h提供文件操作函数常用的包括fopen()- 打开文件fclose()- 关闭文件fseek()- 移动文件指针ftell()- 获取当前文件位置理解这些函数是掌握文件大小获取方法的基础。特别是fseek()和ftell()的组合使用是获取文件大小的经典方法。1.3 文件大小获取方法的分类根据实现原理C 语言中获取文件大小主要有三种方法使用fseek()和ftell()组合- 最常用的可移植方法使用stat()系统调用- 效率最高但系统依赖性较强逐字节读取计数- 教学用途实际项目中不推荐每种方法都有其适用场景和限制接下来我们会详细分析每种方法的实现。2. 使用 fseek 和 ftell 组合获取文件大小2.1 方法原理与实现步骤这种方法的核心思想是将文件指针移动到文件末尾然后获取当前位置的偏移量这个偏移量就是文件的大小。基本步骤如下使用fopen()以二进制模式打开文件使用fseek()将文件指针移动到文件末尾使用ftell()获取当前文件位置即文件大小使用fseek()将文件指针移回文件开头可选便于后续读取关闭文件#include stdio.h long get_file_size(const char* filename) { FILE* file fopen(filename, rb); // 以二进制只读模式打开 if (file NULL) { return -1; // 文件打开失败 } // 移动文件指针到末尾 if (fseek(file, 0, SEEK_END) ! 0) { fclose(file); return -1; } // 获取当前位置文件大小 long size ftell(file); fclose(file); return size; }2.2 关键参数说明与错误处理文件打开模式的重要性必须使用二进制模式rb文本模式在某些系统下会影响文件位置计算如果计划修改文件可以使用 rb 模式错误处理的必要性检查fopen()返回值文件不存在或权限不足时会返回 NULL检查fseek()返回值移动失败时返回非零值确保在任何错误路径上都正确关闭文件改进的错误处理版本#include stdio.h #include errno.h #include string.h long get_file_size_robust(const char* filename) { FILE* file fopen(filename, rb); if (file NULL) { fprintf(stderr, 无法打开文件 %s: %s\n, filename, strerror(errno)); return -1; } if (fseek(file, 0, SEEK_END) ! 0) { fprintf(stderr, 文件定位失败: %s\n, strerror(errno)); fclose(file); return -1; } long size ftell(file); if (size -1) { fprintf(stderr, 获取文件位置失败: %s\n, strerror(errno)); fclose(file); return -1; } fclose(file); return size; }2.3 方法优缺点与适用场景优点可移植性好在所有符合 C 标准的平台上都能工作实现简单代码直观易懂不需要额外的系统特定头文件缺点需要实际打开文件有一定性能开销对于超大文件超过 LONG_MAXftell()可能返回值不准确不适用于无法用fopen()打开的特殊文件适用场景需要跨平台兼容的应用程序处理中小型文件通常小于 2GB开发通用库函数3. 使用 stat 系统调用获取文件大小3.1 stat 函数族介绍stat()系列函数是直接查询文件系统元数据的系统调用可以获取文件的详细信息包括大小、权限、修改时间等。不同系统的 stat 函数Linux/Unix:stat(),fstat(),lstat()Windows:_stat(),_fstat(),_stat64()等相关结构体定义#include sys/stat.h struct stat { dev_t st_dev; // 设备 ID ino_t st_ino; // inode 编号 mode_t st_mode; // 文件类型和权限 nlink_t st_nlink; // 硬链接数 uid_t st_uid; // 所有者用户 ID gid_t st_gid; // 所有者组 ID dev_t st_rdev; // 设备类型如果是设备文件 off_t st_size; // 文件大小字节 // ... 其他时间字段等 };3.2 跨平台实现方案由于不同系统的stat实现有差异需要编写条件编译代码来保证可移植性#include sys/stat.h #ifdef _WIN32 #include windows.h #endif long get_file_size_stat(const char* filename) { struct stat file_stat; // 调用 stat 函数获取文件信息 if (stat(filename, file_stat) -1) { perror(stat 调用失败); return -1; } // 检查是否为普通文件 if (!S_ISREG(file_stat.st_mode)) { fprintf(stderr, %s 不是普通文件\n, filename); return -1; } return (long)file_stat.st_size; }Windows 系统特殊处理#ifdef _WIN32 long get_file_size_windows(const char* filename) { struct _stat file_stat; if (_stat(filename, file_stat) ! 0) { return -1; } return (long)file_stat.st_size; } #endif3.3 处理大文件的支持对于超过 2GB 的大文件需要使用 64 位文件大小支持#include sys/stat.h #ifdef _WIN32 #include windows.h #endif // 返回 64 位文件大小 long long get_large_file_size(const char* filename) { #ifdef _WIN32 struct _stat64 file_stat; if (_stat64(filename, file_stat) ! 0) { return -1; } #else struct stat file_stat; if (stat(filename, file_stat) -1) { return -1; } #endif return file_stat.st_size; }3.4 性能对比与选择建议性能特点对比方法性能精度跨平台性大文件支持fseek/ftell中等受 long 类型限制优秀有限stat优秀精确良好需适配优秀64位版本选择建议追求性能优先选择stat()方法需要处理超大文件使用 64 位版本的stat简单跨平台使用fseek()/ftell()组合特殊文件如管道可能需要其他方法4. 实际应用与完整示例4.1 完整的文件大小获取工具下面是一个完整的示例程序演示了多种获取文件大小的方法#include stdio.h #include stdlib.h #include string.h #include errno.h #include sys/stat.h // 方法1: 使用 fseek/ftell long get_size_method1(const char* filename) { FILE* file fopen(filename, rb); if (!file) return -1; fseek(file, 0, SEEK_END); long size ftell(file); fclose(file); return size; } // 方法2: 使用 stat long get_size_method2(const char* filename) { struct stat file_stat; if (stat(filename, file_stat) -1) { return -1; } return (long)file_stat.st_size; } // 统一的文件大小获取函数 long get_file_size(const char* filename, int method) { switch (method) { case 1: return get_size_method1(filename); case 2: return get_size_method2(filename); default: return -1; } } int main(int argc, char* argv[]) { if (argc ! 2) { printf(用法: %s 文件名\n, argv[0]); return 1; } const char* filename argv[1]; printf(检查文件: %s\n, filename); // 尝试两种方法 long size1 get_file_size(filename, 1); long size2 get_file_size(filename, 2); if (size1 -1) { printf(方法1失败: 无法获取文件大小\n); } else { printf(方法1 (fseek/ftell): %ld 字节 (%.2f KB, %.2f MB)\n, size1, size1/1024.0, size1/(1024.0*1024.0)); } if (size2 -1) { printf(方法2失败: 无法获取文件大小\n); } else { printf(方法2 (stat): %ld 字节 (%.2f KB, %.2f MB)\n, size2, size2/1024.0, size2/(1024.0*1024.0)); } // 验证结果一致性 if (size1 ! -1 size2 ! -1 size1 ! size2) { printf(警告: 两种方法结果不一致\n); } return 0; }4.2 编译与测试编译命令# Linux/Mac gcc -o filesize filesize.c # Windows (MinGW) gcc -o filesize.exe filesize.c测试用例# 创建测试文件 echo 这是一个测试文件 test.txt # 运行程序 ./filesize test.txt ./filesize /etc/passwd # 系统文件 ./filesize /nonexistent # 不存在的文件4.3 实际应用场景示例场景1读取整个文件到内存#include stdio.h #include stdlib.h #include string.h char* read_entire_file(const char* filename) { FILE* file fopen(filename, rb); if (!file) return NULL; fseek(file, 0, SEEK_END); long size ftell(file); fseek(file, 0, SEEK_SET); if (size 0) { fclose(file); return NULL; } char* buffer malloc(size 1); if (!buffer) { fclose(file); return NULL; } size_t read_size fread(buffer, 1, size, file); buffer[read_size] \0; // 添加字符串结束符 fclose(file); if (read_size ! (size_t)size) { free(buffer); return NULL; } return buffer; }场景2文件大小限制检查#include stdbool.h bool is_file_size_valid(const char* filename, long max_size) { long size get_file_size(filename, 2); // 使用 stat 方法 if (size -1) { return false; // 文件不存在或无法访问 } return size max_size; }5. 常见问题与解决方案5.1 文件大小获取失败的原因分析问题现象可能原因解决方案返回 -1文件不存在检查文件路径和权限返回 0文件为空或符号链接使用 stat 检查文件类型结果不一致文本模式打开文件使用二进制模式 (rb)数值异常文件正在被修改确保文件未被其他进程独占5.2 大文件处理注意事项32 位系统的限制long类型通常为 4 字节最大表示 2GB使用long long或off_t处理大文件考虑使用 64 位版本的函数大文件安全代码示例#include stdint.h int64_t get_file_size64(const char* filename) { #ifdef _WIN32 struct _stat64 file_stat; if (_stat64(filename, file_stat) ! 0) { return -1; } return file_stat.st_size; #else struct stat file_stat; if (stat(filename, file_stat) -1) { return -1; } return (int64_t)file_stat.st_size; #endif }5.3 跨平台兼容性处理条件编译的最佳实践#if defined(_WIN32) || defined(_WIN64) #define PLATFORM_WINDOWS 1 #elif defined(__linux__) #define PLATFORM_LINUX 1 #elif defined(__APPLE__) #define PLATFORM_MAC 1 #else #define PLATFORM_UNKNOWN 1 #endif #if PLATFORM_WINDOWS #include windows.h #define STAT_FUNC _stat64 #define STAT_STRUCT _stat64 #else #include sys/stat.h #define STAT_FUNC stat #define STAT_STRUCT stat #endif5.4 性能优化建议避免重复获取文件大小如果程序需要多次使用同一文件大小缓存结果对于频繁访问的文件考虑使用内存映射选择合适的方法单次查询使用stat()方法已打开文件使用fstat()避免重复打开需要文件句柄使用fseek()/ftell()组合6. 最佳实践与扩展应用6.1 生产环境中的文件大小获取在生产环境中除了基本的功能实现还需要考虑错误处理与日志记录#include time.h long get_file_size_production(const char* filename) { struct stat file_stat; time_t start_time time(NULL); if (stat(filename, file_stat) -1) { // 记录详细的错误信息 fprintf(stderr, [ERROR] %s: 无法获取文件 %s 的大小: %s\n, ctime(start_time), filename, strerror(errno)); return -1; } if (!S_ISREG(file_stat.st_mode)) { fprintf(stderr, [WARNING] %s: %s 不是普通文件\n, ctime(start_time), filename); return -1; } printf([INFO] %s: 成功获取文件 %s 大小: %ld 字节\n, ctime(start_time), filename, (long)file_stat.st_size); return (long)file_stat.st_size; }6.2 文件大小获取的性能基准测试对于性能敏感的应用可以编写简单的基准测试#include time.h void benchmark_file_size_methods(const char* filename, int iterations) { clock_t start, end; double time_used; // 测试方法1: fseek/ftell start clock(); for (int i 0; i iterations; i) { get_size_method1(filename); } end clock(); time_used ((double)(end - start)) / CLOCKS_PER_SEC; printf(方法1 (fseek/ftell): %d 次耗时 %.4f 秒\n, iterations, time_used); // 测试方法2: stat start clock(); for (int i 0; i iterations; i) { get_size_method2(filename); } end clock(); time_used ((double)(end - start)) / CLOCKS_PER_SEC; printf(方法2 (stat): %d 次耗时 %.4f 秒\n, iterations, time_used); }6.3 扩展应用目录大小计算基于文件大小获取可以进一步实现目录大小计算#include dirent.h #ifdef _WIN32 #include windows.h #endif long long calculate_directory_size(const char* dirpath) { long long total_size 0; DIR* dir opendir(dirpath); if (!dir) { return -1; } struct dirent* entry; while ((entry readdir(dir)) ! NULL) { // 跳过 . 和 .. 目录 if (strcmp(entry-d_name, .) 0 || strcmp(entry-d_name, ..) 0) { continue; } // 构建完整路径 char fullpath[1024]; snprintf(fullpath, sizeof(fullpath), %s/%s, dirpath, entry-d_name); struct stat file_stat; if (stat(fullpath, file_stat) -1) { continue; } if (S_ISREG(file_stat.st_mode)) { total_size file_stat.st_size; } else if (S_ISDIR(file_stat.st_mode)) { long long subdir_size calculate_directory_size(fullpath); if (subdir_size ! -1) { total_size subdir_size; } } } closedir(dir); return total_size; }6.4 安全注意事项路径遍历攻击防护#include libgen.h bool is_safe_path(const char* filename) { // 检查是否包含危险的路径遍历模式 if (strstr(filename, ../) ! NULL || strstr(filename, ..\\) ! NULL) { return false; } // 检查绝对路径是否在允许的目录内 char* resolved_path realpath(filename, NULL); if (!resolved_path) { return false; } // 验证路径是否在安全目录内 bool is_safe (strncmp(resolved_path, /safe/directory/, 16) 0); free(resolved_path); return is_safe; }掌握 C 语言中获取文件大小的方法只是文件处理的基础在实际项目中还需要结合具体的应用场景选择最合适的方案。对于性能要求高的场景优先使用 stat 方法对于需要最大可移植性的情况使用 fseek/ftell 组合处理超大文件时注意使用 64 位版本。正确的错误处理和边界条件检查是保证程序健壮性的关键。

相关新闻

微信JSSDK开发实战:从入门到企业级应用

微信JSSDK开发实战:从入门到企业级应用

1. 什么是JSSDK及其核心价值JSSDK(JavaScript Software Development Kit)是平台方提供给网页开发者的工具包,它架起了网页与原生应用之间的桥梁。以微信JSSDK为例,它让H5页面能够调用微信客户端的原生能力,比如&#x…

2026/7/16 9:52:34 阅读更多 →
Linus Torvalds谈AI编程:辅助工具与系统架构思维的差距

Linus Torvalds谈AI编程:辅助工具与系统架构思维的差距

1. Linus Torvalds对AI编程的核心观点解析在2023年Linux基金会的一次技术访谈中,Linux之父Linus Torvalds罕见地谈到了对AI编程工具的看法。这位以直言不讳著称的技术领袖表示:"当前AI可以生成看似合理的代码,但它不理解代码背后的设计意…

2026/7/16 9:50:33 阅读更多 →
Anaconda保姆级安装与环境配置全攻略(2025版)

Anaconda保姆级安装与环境配置全攻略(2025版)

1. 为什么选择Anaconda? 如果你刚接触Python数据分析或机器学习,一定会被各种库的安装和版本兼容问题搞得头大。我第一次用Python做数据可视化时,光是安装matplotlib就折腾了一下午——不是缺依赖就是版本冲突。直到发现了Anaconda这个神器&a…

2026/7/16 9:48:32 阅读更多 →

最新新闻

【AI大模型】涌现能力:大模型为什么参数越大越聪明

【AI大模型】涌现能力:大模型为什么参数越大越聪明

【AI大模型】涌现能力:大模型为什么参数越大越聪明(含实操代码) 在AI大模型迭代升级的过程中,所有人都能直观发现一个核心规律:模型参数规模越小,能力越平庸,只能完成简单的分词、分类、短句生成任务;而当参数规模突破百亿、千亿级别后,模型会突然解锁海量新能力,逻…

2026/7/16 10:55:19 阅读更多 →
CANN/asc-devkit:SetFixPipeClipRelu寄存器配置

CANN/asc-devkit:SetFixPipeClipRelu寄存器配置

# SetFixPipeClipRelu 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。 项目地址…

2026/7/16 10:55:19 阅读更多 →
C++内存池原理与性能优化实践

C++内存池原理与性能优化实践

1. 为什么需要内存池?在C/C开发中,我们最常听到的崩溃原因就是"内存泄漏"和"内存碎片"。每次直接调用malloc/free或new/delete时,操作系统都需要在堆内存中寻找合适大小的空闲块。当频繁进行小块内存的申请释放时&#x…

2026/7/16 10:53:18 阅读更多 →
Toggl Track 浏览器扩展架构解析:从时间跟踪到深度集成

Toggl Track 浏览器扩展架构解析:从时间跟踪到深度集成

Toggl Track 浏览器扩展架构解析:从时间跟踪到深度集成 【免费下载链接】track-extension Toggl Track browser extension for Chrome and Firefox 项目地址: https://gitcode.com/gh_mirrors/tr/track-extension Toggl Track 浏览器扩展作为开源的时间跟踪工…

2026/7/16 10:53:18 阅读更多 →
开源桌面伴侣框架:打造智能交互的数字生活新体验

开源桌面伴侣框架:打造智能交互的数字生活新体验

开源桌面伴侣框架:打造智能交互的数字生活新体验 【免费下载链接】DyberPet Desktop Cyber Pet Framework based on PySide6 项目地址: https://gitcode.com/GitHub_Trending/dy/DyberPet 你是否曾想象过,桌面上的二次元角色不仅能与你互动&#…

2026/7/16 10:53:18 阅读更多 →
红外遥控接收头原理、选型与电路设计指南

红外遥控接收头原理、选型与电路设计指南

1. 红外遥控接收头的基础原理与核心参数红外遥控接收头是现代电子设备中不可或缺的组件,它承担着将红外光信号转换为电信号的关键任务。这类器件通常由光电二极管、前置放大器、带通滤波器和解调电路集成在一个小型封装内。当接收到特定频率(通常是38kHz…

2026/7/16 10:47:16 阅读更多 →

日新闻

HarmonyOs应用《重要日》开发第6篇 - 数据持久化存储

HarmonyOs应用《重要日》开发第6篇 - 数据持久化存储

本篇深入剖析 ImportantDays 项目的数据持久化方案——基于 HarmonyOS ArkData 模块的 Preferences 轻量级存储,以及 PreferenceUtil 工具类的单例封装。一、HarmonyOS 数据存储方案对比 HarmonyOS 提供了多种数据存储方案:方案适用场景特点Preferences轻…

2026/7/16 0:08:27 阅读更多 →
Python实现跨境电商商品图批量翻译教程

Python实现跨境电商商品图批量翻译教程

一、问题引入做跨境电商的卖家朋友,你是否遇到过这样的困扰?每次上架新品到亚马逊、Shopee或Lazada等平台,都需要处理大量商品图片的多语言版本。比如上架200款衣服,每款需要翻译成英语、日语、韩语等5种语言,这意味着…

2026/7/16 0:08:27 阅读更多 →
鸿蒙 7 新特性实战①:从 0 到 1 掌握 @kit 标准导入规范

鸿蒙 7 新特性实战①:从 0 到 1 掌握 @kit 标准导入规范

从鸿蒙 7(HarmonyOS NEXT)开始,官方全面完成了从 ohos.* 零散模块到 kit.* 领域套件的体系重构。对开发者来说,第一道门槛不是 API 用法变化,而是统一的导入规范——旧体系默认导入、解构导入混用的混乱局面被彻底终结…

2026/7/16 0:10:29 阅读更多 →

周新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/15 21:09:01 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/15 19:42:20 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/15 17:52:08 阅读更多 →

月新闻