尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C语言文件大小获取:ftell与fseek的健壮实现与跨平台方案
在C语言开发中获取文件大小是一个常见但容易被忽视的技术细节。虽然标准库没有直接提供获取文件大小的函数但通过ftell()和fseek()的组合使用我们可以轻松实现这一功能。本文将详细介绍三种可靠的实现方法并分析各自的适用场景和注意事项。文件大小获取在文件传输、内存分配和磁盘空间检查等场景中至关重要。一个健壮的实现不仅要准确返回文件字节数还要保证后续文件操作的正常进行。我们将从基础实现开始逐步深入到生产环境可用的完整方案。1. 核心能力速览能力项说明实现方式基于标准C库函数组合核心函数fseek(),ftell(),fgetpos(),fsetpos()文件模式必须使用二进制模式打开平台兼容Windows/Linux/macOS通用文件大小限制受限于long int类型范围通常2GB内存占用极低仅栈上几个变量适用场景本地文件处理、嵌入式系统、跨平台开发2. 适用场景与使用边界获取文件大小的功能在以下场景中特别有用适用场景文件上传前的空间检查确保目标位置有足够空间内存映射文件需要知道确切的文件大小来创建映射文件分块处理按固定大小分块读取大文件进度显示计算文件传输或处理的进度百分比缓冲区分配根据文件大小动态分配读取缓冲区使用边界限制文件必须存在且可读否则会返回错误二进制模式是必须的文本模式可能导致不准确的结果对于超过2GB的大文件需要考虑使用fseeko()和ftello()等64位版本网络文件系统或特殊设备文件可能不支持seek操作3. 环境准备与前置条件在开始编码前需要确保开发环境满足基本要求开发环境要求任何支持C99标准的编译器GCC、Clang、MSVC等标准C库stdio.h、stdlib.h基本的文件系统访问权限代码包含的头文件#include stdio.h // 文件操作函数 #include stdlib.h // exit()函数 #include errno.h // 错误处理可选文件打开模式注意事项必须使用二进制模式rb、rb等文本模式r、w等在Windows平台可能因换行符转换导致大小计算错误文件指针必须有效且文件必须成功打开4. 基础实现方法分析4.1 最简单的实现方式最基本的文件大小获取方法使用fseek()和ftell()组合long get_file_size_basic(FILE *fp) { if (fp NULL) { return -1; // 错误处理 } fseek(fp, 0, SEEK_END); // 移动到文件末尾 long size ftell(fp); // 获取当前位置即文件大小 return size; }这种方法的问题移动了文件内部指针影响后续读写操作没有错误检查如果seek失败会返回错误结果不适合在需要连续文件操作的场景中使用4.2 问题演示代码void demonstrate_problem() { FILE *fp fopen(test.dat, rb); if (fp NULL) { perror(Failed to open file); return; } // 获取文件大小指针移动到末尾 long size get_file_size_basic(fp); printf(File size: %ld bytes\n, size); // 尝试读取文件内容会失败 char buffer[100]; size_t read_count fread(buffer, 1, sizeof(buffer), fp); printf(Read %zu bytes after size check\n, read_count); // 输出0 fclose(fp); }5. 健壮的实现方案5.1 使用文件位置保存/恢复为了解决基础实现的问题我们需要在获取大小前保存当前位置获取后恢复位置long get_file_size_robust(FILE *fp) { if (fp NULL) { return -1; } long current_pos ftell(fp); // 保存当前位置 if (current_pos -1) { return -1; // 获取当前位置失败 } if (fseek(fp, 0, SEEK_END) ! 0) { return -1; // 移动到末尾失败 } long size ftell(fp); if (size -1) { return -1; // 获取大小失败 } // 恢复原始位置 if (fseek(fp, current_pos, SEEK_SET) ! 0) { return -1; // 恢复位置失败 } return size; }5.2 使用fpos_t的增强版本对于需要更高精度的场景可以使用fgetpos()和fsetpos()long get_file_size_enhanced(FILE *fp) { if (fp NULL) { return -1; } fpos_t original_pos; if (fgetpos(fp, original_pos) ! 0) { return -1; // 获取当前位置失败 } if (fseek(fp, 0, SEEK_END) ! 0) { return -1; // 移动到末尾失败 } long size ftell(fp); if (size -1) { return -1; } if (fsetpos(fp, original_pos) ! 0) { return -1; // 恢复位置失败 } return size; }6. 完整示例程序下面是一个完整的测试程序演示各种情况下的文件大小获取#include stdio.h #include stdlib.h #include string.h // 健壮的文件大小获取函数 long get_file_size(const char *filename) { if (filename NULL) { fprintf(stderr, Error: filename is NULL\n); return -1; } FILE *fp fopen(filename, rb); if (fp NULL) { perror(Failed to open file); return -1; } long current_pos ftell(fp); if (fseek(fp, 0, SEEK_END) ! 0) { perror(fseek failed); fclose(fp); return -1; } long size ftell(fp); if (size -1) { perror(ftell failed); fclose(fp); return -1; } // 恢复位置 fseek(fp, current_pos, SEEK_SET); // 验证文件操作是否正常 char buffer[1]; if (fread(buffer, 1, 1, fp) ! 1 !feof(fp)) { fprintf(stderr, Warning: file operation may be compromised\n); } fclose(fp); return size; } // 创建测试文件 void create_test_file(const char *filename, size_t size) { FILE *fp fopen(filename, wb); if (fp NULL) { perror(Failed to create test file); return; } // 写入指定大小的数据 for (size_t i 0; i size; i) { fputc(A (i % 26), fp); } fclose(fp); printf(Created test file: %s (%zu bytes)\n, filename, size); } int main() { const char *test_files[] { small_file.txt, medium_file.dat, large_file.bin }; size_t test_sizes[] {100, 10240, 102400}; // 创建测试文件 for (int i 0; i 3; i) { create_test_file(test_files[i], test_sizes[i]); } // 测试文件大小获取 printf(\nFile size test results:\n); printf(\n); for (int i 0; i 3; i) { long size get_file_size(test_files[i]); if (size ! -1) { printf(%s: %ld bytes (expected: %zu) %s\n, test_files[i], size, test_sizes[i], (size test_sizes[i]) ? ✓ : ✗); } } // 测试错误情况 printf(\nError handling tests:\n); printf(\n); // 测试不存在的文件 long size get_file_size(non_existent_file.txt); printf(Non-existent file: %ld (should be -1) %s\n, size, (size -1) ? ✓ : ✗); // 测试空文件名 size get_file_size(NULL); printf(NULL filename: %ld (should be -1) %s\n, size, (size -1) ? ✓ : ✗); return 0; }7. 大文件处理方案对于超过2GB的大文件需要使用64位文件操作函数7.1 使用fseeko和ftelloPOSIX标准#ifdef __unix__ #include sys/types.h off_t get_large_file_size(const char *filename) { FILE *fp fopen(filename, rb); if (fp NULL) { return -1; } if (fseeko(fp, 0, SEEK_END) ! 0) { fclose(fp); return -1; } off_t size ftello(fp); fclose(fp); return size; } #endif7.2 Windows平台的大文件支持#ifdef _WIN32 #include windows.h __int64 get_large_file_size_win(const char *filename) { HANDLE hFile CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile INVALID_HANDLE_VALUE) { return -1; } LARGE_INTEGER size; if (!GetFileSizeEx(hFile, size)) { CloseHandle(hFile); return -1; } CloseHandle(hFile); return size.QuadPart; } #endif8. 性能优化与最佳实践8.1 缓存文件大小信息对于需要频繁获取文件大小的场景可以考虑缓存结果typedef struct { char filename[256]; long size; time_t last_modified; } file_cache_t; long get_file_size_cached(const char *filename, file_cache_t *cache) { if (cache ! NULL strcmp(cache-filename, filename) 0) { struct stat st; if (stat(filename, st) 0 st.st_mtime cache-last_modified) { return cache-size; // 返回缓存结果 } } // 重新获取文件大小 long size get_file_size(filename); // 更新缓存 if (cache ! NULL size ! -1) { strncpy(cache-filename, filename, sizeof(cache-filename) - 1); cache-size size; struct stat st; if (stat(filename, st) 0) { cache-last_modified st.st_mtime; } } return size; }8.2 错误处理最佳实践完善的错误处理能让代码更加健壮typedef enum { FILE_SIZE_SUCCESS 0, FILE_SIZE_ERROR_NULL_PTR, FILE_SIZE_ERROR_OPEN_FAILED, FILE_SIZE_ERROR_SEEK_FAILED, FILE_SIZE_ERROR_TELL_FAILED, FILE_SIZE_ERROR_RESTORE_FAILED } file_size_result_t; file_size_result_t get_file_size_detailed(const char *filename, long *result) { if (filename NULL || result NULL) { return FILE_SIZE_ERROR_NULL_PTR; } FILE *fp fopen(filename, rb); if (fp NULL) { return FILE_SIZE_ERROR_OPEN_FAILED; } long current_pos ftell(fp); if (fseek(fp, 0, SEEK_END) ! 0) { fclose(fp); return FILE_SIZE_ERROR_SEEK_FAILED; } long size ftell(fp); if (size -1) { fclose(fp); return FILE_SIZE_ERROR_TELL_FAILED; } if (fseek(fp, current_pos, SEEK_SET) ! 0) { fclose(fp); return FILE_SIZE_ERROR_RESTORE_FAILED; } fclose(fp); *result size; return FILE_SIZE_SUCCESS; }9. 常见问题与排查方法问题现象可能原因排查方式解决方案返回大小为0文件打开失败或为空文件检查fopen返回值验证文件是否存在确保文件路径正确有读取权限返回负数函数执行失败检查每个步骤的返回值添加详细的错误处理代码文本文件大小不准确在文本模式下打开检查文件打开模式使用rb而不是r大文件大小错误超过long int范围检查文件实际大小使用64位文件操作函数后续文件操作异常文件指针未恢复验证fseek恢复操作确保在返回前恢复文件指针性能问题频繁调用大小获取分析调用频率实现缓存机制10. 实际应用场景扩展10.1 文件分块读取获取文件大小后可以实现高效的分块处理int process_file_in_chunks(const char *filename, size_t chunk_size) { long total_size get_file_size(filename); if (total_size 0) { return -1; } FILE *fp fopen(filename, rb); if (fp NULL) { return -1; } char *buffer malloc(chunk_size); if (buffer NULL) { fclose(fp); return -1; } size_t chunks (total_size chunk_size - 1) / chunk_size; for (size_t i 0; i chunks; i) { size_t offset i * chunk_size; size_t bytes_to_read chunk_size; if (offset chunk_size total_size) { bytes_to_read total_size - offset; } fseek(fp, offset, SEEK_SET); size_t read_bytes fread(buffer, 1, bytes_to_read, fp); if (read_bytes ! bytes_to_read) { fprintf(stderr, Read error at chunk %zu\n, i); break; } // 处理当前块数据 process_chunk(buffer, read_bytes, i); // 显示进度 printf(Progress: %.1f%%\r, (float)(i 1) * 100 / chunks); fflush(stdout); } free(buffer); fclose(fp); return 0; }10.2 内存映射文件准备在创建内存映射前需要准确的文件大小#ifdef __unix__ #include sys/mman.h void* create_memory_map(const char *filename) { int fd open(filename, O_RDONLY); if (fd -1) { perror(Failed to open file for mapping); return NULL; } // 获取文件大小 struct stat st; if (fstat(fd, st) -1) { perror(Failed to get file size); close(fd); return NULL; } if (st.st_size 0) { fprintf(stderr, File is empty\n); close(fd); return NULL; } void *map mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (map MAP_FAILED) { perror(Memory mapping failed); close(fd); return NULL; } close(fd); return map; } #endif通过本文介绍的多种文件大小获取方法你可以根据具体需求选择最适合的实现方案。无论是简单的脚本工具还是复杂的生产系统正确的文件大小处理都是确保程序稳定性的重要环节。
RELATED

相关推荐

ArcGIS 10.8 保姆级安装与避坑指南:从环境准备到成功启动

ArcGIS 10.8 保姆级安装与避坑指南:从环境准备到成功启动

1. 环境准备:避开90%新手踩过的坑ArcGIS 10.8对系统环境的要求比早期版本严格得多。我见过太多人兴冲冲下载完安装包,结果卡在第一步系统检测。先别急着双击安装程序,花5分钟做好这些准备能省下几小时折腾时间。操作系统兼容性检查是首要任务…

📅 2026/9/15 14:54:18
nanoScrollerJS核心功能解析:让网页滚动体验提升10倍的秘密

nanoScrollerJS核心功能解析:让网页滚动体验提升10倍的秘密

nanoScrollerJS核心功能解析:让网页滚动体验提升10倍的秘密 【免费下载链接】nanoScrollerJS A jQuery plugin that offers a simplistic way of implementing Lion OS scrollbars. 项目地址: https://gitcode.com/gh_mirrors/na/nanoScrollerJS nanoScrolle…

📅 2026/10/22 11:56:20
Gemma-4-e4b-it-mxfp8模型微调指南:自定义数据集训练技巧

Gemma-4-e4b-it-mxfp8模型微调指南:自定义数据集训练技巧

Gemma-4-e4b-it-mxfp8模型微调指南:自定义数据集训练技巧 【免费下载链接】gemma-4-e4b-it-mxfp8 项目地址: https://ai.gitcode.com/hf_mirrors/mlx-community/gemma-4-e4b-it-mxfp8 在当今AI快速发展的时代,Gemma-4-e4b-it-mxfp8模型为开发者和…

📅 2026/10/6 18:33:23
MORE NEWS

更多资讯

📰

全国水系矢量图(ArcGIS可打开):CGCS2000_Albers投影的权威河流线数据

简介:本资源为全国水系矢量数据集,面向GIS初学者、地理信息专业学生、城乡规划及环境研究从业者,解决基础空间分析中缺乏权威、可直接加载的全国尺度水体底图问题。压缩包共27个文件,含6个.shp(水系几何线/面&#xff…

📰

视频生成推理加速工程:多路优化与可插拔配置系统

做视频生成推理优化的同学最近应该都感受到了同一个矛盾:分辨率上去了、时长拉长了,单条样本的生成耗时却成倍往上翻。围绕这个矛盾,本文整理一套可落地的加速工程笔记,重点不是某个具体分支的内部细节,而是把“多路加…

📰

VOC2007数据集实战:从解压到训练,避坑与验证全流程

简介:面向计算机视觉初学者与算法研究者的PASCAL VOC2007数据集完整包,可用于图像分类、物体检测与语义分割等深度学习任务的训练与评估,尤其适合作为目标检测算法的入门基准与模型微调实验素材。压缩包共收录5405个文件,约30.14M…

📰

DeepSeek-V3技术报告解读:MoE架构、FP8训练与成本优化

简介:《DeepSeek-V3技术报告》是DeepSeek-AI团队发布的671B参数混合专家(MoE)语言模型官方技术文献,适合大模型研究者、AI工程师及对前沿架构感兴趣的开发者阅读。报告系统解析了多头潜在注意力(MLA)、Deep…

📰

牛客平台数据三视图系统设计与性能优化实战

1. 项目背景与核心价值三视图这个概念在技术社区里其实有两种完全不同的解读路径。一种是传统机械制图中的正视图、俯视图、侧视图,另一种则是互联网从业者熟悉的"数据三视图"——即用户行为轨迹、系统日志和业务指标的交叉观测。今天要讨论的显然是后者&…

📰

使用srvany.exe将普通程序转为Windows服务的完整指南

1. 为什么需要srvany.exe创建Windows服务在Windows系统中,许多后台程序需要以服务形式运行才能实现开机自启、崩溃重启等关键功能。但并非所有程序都原生支持服务化运行,这时候srvany.exe就派上了用场。srvany是Windows Server Resource Kit工具包中的一…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬