Android平台下使用std::ifstream获取文件大小返回0的解决方法
Android 平台基于
std::ifstream 获取文件大小的可靠实现 现有代码的失效原因
原实现拿到0值并非随机问题,是三类常见的平台适配坑叠加导致的,其中也包含明确的格式层面问题:
- 低版本STL对
ios::ate标记的实现缺陷
旧版NDK内置的STL中,打开文件时附带std::ios::ate标记并不会稳定把文件指针定位到末尾,多数场景下指针仍停留在文件起始位置,此时调用tellg自然返回0。 - 变参传递的类型不匹配(格式层面问题)
tellg()返回值是std::streampos类类型,并非原生整数。代码中使用%ld占位符期待接收一个long类型的整数,实际传入的是类类型的非POD值,二者ABI规则不兼容。直接将该类型作为可变参数传入__android_log_print这类C风格变参函数时,ARM架构下会将传参值处理成0,完全丢失真实的位置信息。 - 流状态校验缺失
仅通过is_open()判断文件可访问并不充分:当流因为权限不足、文件被截断、磁盘IO错误进入fail/bad状态时,tellg会返回无效位置,现有代码不会触发错误分支就直接使用返回值。
全版本兼容的可靠实现
这套方案不需要依赖std::filesystem,从API 16到最新Android版本均可稳定运行:
std::ifstream f(filePath, std::ios::binary); if (!f.is_open()) { ALOGV(TAG, "problem opening file"); return; } // 主动定位到文件末尾,兼容低版本STL的ate标记实现缺陷 f.seekg(0, std::ios::end); if (f.fail()) { ALOGV(TAG, "seek to file end failed"); f.close(); return; } std::streampos endPos = f.tellg(); // 校验位置获取是否有效 if (endPos == std::streampos(-1)) { ALOGV(TAG, "tellg failed to read file position"); f.close(); return; } // 显式转为原生整数类型,适配32/64位架构 auto fileSize = static_cast<long long>(endPos); f.close(); __android_log_print(ANDROID_LOG_VERBOSE, "my tag", "filesize: %lld", fileSize);
编译与使用注意事项
- 工程编译时统一选择
c++_static或c++_shared作为STL实现,废弃已停止维护的gnustl_shared,该STL的文件流定位逻辑存在已知bug - 访问应用外部存储路径时,需确认已申请对应存储权限,否则哪怕文件真实存在,流也会进入不可用的fail状态
- 测量文件大小的流程中不要穿插其他文件读写操作,避免流状态被污染
内容的提问来源于stack exchange,提问作者Gatonito
相关产品推荐
相关产品推荐

