相同C++代码MSVC编译读文件异常,MinGW编译可正常读取
同一C++20代码在GCC与MSVC下文件读取行为不一致的原因
问题现象
一段读取zip文件的C代码,在Cygwin环境下使用GCC 11.3.0以C20标准编译后,可完整读取asio-1.28.0.zip文件,最终触发eofbit和failbit;但相同代码用Visual Studio 2022的MSVC编译器以C++20标准编译后,生成的程序仅读取414个字符就停止,同样触发eofbit和failbit,且所用zip文件为同一来源。
测试代码
#include <iostream> #include <fstream> using namespace std; int main() { ifstream infile {"asio-1.28.0.zip", ios_base::in}; char buf[512]; bool done = false; while ((!done) && (infile.rdstate() == ios::goodbit)) { infile.read(buf, 512); cout << infile.rdstate() << endl; size_t len = infile.gcount(); if (len < 512) done=true; cout << "Read " << len << " chars\n"; } if (infile.eof()) { cout << "Found eofbit\n"; } if (infile.fail()) { cout << "Found failbit\n"; } if (infile.bad()) { cout << "Found badbit\n"; } return 0; }
编译命令
GCC(Cygwin环境)
g++ --std=c++20 file_read_write.cpp -o file_read_write_gcc.exe
MSVC(Visual Studio 2022)
cl /std:c++20 file_read_write.cpp
原因分析
这不是编译器Bug,是代码中忽略了二进制文件的正确打开模式导致的平台行为差异:
- zip文件属于二进制文件,而你仅用
ios_base::in打开文件,在Windows原生环境(MSVC运行环境)下,默认会以文本模式处理文件。文本模式下,Windows会将0x1A(DOS系统的EOF标记字符)视为文件结束标志,而你的zip文件恰好在前414字节位置包含该字符,因此MSVC的ifstream会提前终止读取。 - Cygwin环境模拟POSIX系统,其文本模式与二进制模式的差异极小,不会将
0x1A识别为EOF,因此GCC编译的程序能完整读取整个文件。
修正方案
打开二进制文件时,必须明确指定ios_base::binary模式,确保以字节流方式读取,不处理特殊字符。修改文件打开代码如下:
ifstream infile {"asio-1.28.0.zip", ios_base::in | ios_base::binary};
修改后,无论使用GCC还是MSVC编译,程序都会一致地完整读取zip文件。
内容的提问来源于stack exchange,提问作者Sudarshan Choubey
相关产品推荐
相关产品推荐

