C++ 一次读取一个文件块时出现一个关闭错误

C++ 一次读取一个文件块时出现一个关闭错误,c++,file,c++17,file-read,C++,File,C++17,File Read,由于实现定义的问题,ifstream::readsome对于读取文件块来说是出了名的不好。在我的例子中,MSVC在新打开的文件上返回0 我查看了它们的实现,发现它们只是在幕后调用ifstream::read: MSVC实施 streamsize\u CLR\u或\u THIS\u调用readsome(\u Elem*\u Str, streamsize _Count){//将最多_Count个字符读入缓冲区,而不阻塞 ios_base::iostate _State=ios_base::good

由于实现定义的问题,
ifstream::readsome
对于读取文件块来说是出了名的不好。在我的例子中,MSVC在新打开的文件上返回0

我查看了它们的实现,发现它们只是在幕后调用
ifstream::read

MSVC实施

streamsize\u CLR\u或\u THIS\u调用readsome(\u Elem*\u Str,
streamsize _Count){//将最多_Count个字符读入缓冲区,而不阻塞
ios_base::iostate _State=ios_base::goodbit;
_Chcount=0;
警察哨兵_Ok(*这是真的);
流大小_Num;
如果(!\u正常){
_状态|=ios_base::failbit;//无缓冲区,失败
}如果((_Num=_Myios::rdbuf()->in_avail())<0){
_状态|=ios_base::eofbit;//没有可用字符
}如果(0<\u计数和&0<\u数值){//读取可用,则为else
读取(\u Str,\u Num<\u Count?\u Num:\u Count);
}
_Myios::设置状态(_状态);
返回gcount();
}
所以我实现了自己的,只调用
ifstream::read

我的实施

std::可选ReadSomeStringFromFile(std::ifstream&ifs,std::streampos pos,std::streamsize count)无例外{
if(ifs&&ifs.is_open()){
自动结果=标准::字符串(计数,'\0');
如果是seekg(位置);
ifs.read(result.data(),count);
if(ifs.gcount()){
返回结果;
}
}
返回{};
}
用法:

std::streampos pos{0};
std::streamsize计数{10};
std::ifstream ifs{g_options_filepath};
{
auto stream=FileUtils::ReadSomeStringFromFile(ifs、pos、count);
while(stream.has_value()){
DebuggerPrintf(stream.value().c_str());
pos+=计数;
stream=FileUtils::ReadSomeStringFromFile(ifs、pos、count);
}
}
这适用于二进制文件(我有一个单独的函数),但对于需要保留换行符的字符串版本,如果块包含换行符,则会产生一个off-by-one错误。这将导致块中的最后一个字符复制为下一个块中的第一个字符:

预期产出

difficulty=Normal
controlpref=Mouse
sound=5
music=5
cameraShakeStrength=1.000000
difficulty=Normal
coontrolpref=Mouse
souund=5
musiic=5
camerraShakeStrength=1.000000
实际产出

difficulty=Normal
controlpref=Mouse
sound=5
music=5
cameraShakeStrength=1.000000
difficulty=Normal
coontrolpref=Mouse
souund=5
musiic=5
camerraShakeStrength=1.000000
默认情况下,使用格式化的
ifstream::get
将换行符用作分隔符并完全跳过它(同样,需要保留换行符),并导致交叉输出和删除字符:

difficult=Normalontrolpre=Mouseund=5ic=5raShakeStength=1.00000
问题


有没有办法尝试在格式化数据上使用非格式化输入函数,或者我不应该在文本数据上使用它?

我不经常使用
get
,所以我忘记了它的存在。使用我想出了一个解决方案:

(我仔细检查了一下,另一个答案使用了
FormattedInput
作为
(ifs>>std::noskipws>>ch)
给出了相同的结果,即使
get
规范说它将其视为
未格式化输入

[[nodiscard]]std::可选ReadSomeStringBufferFromFile(std::ifstream&ifs,std::streampos pos,std::streamsize count/*=0u*/)无例外{
if(!(ifs&&ifs.is_open()){
返回{};
}
ifs.seekg(位置、标准::ios::beg);
//我想早点离开这里,
//但是MSVC ifstream::seekg没有设置失败位,
//所以在接到电话之前不能早出去。
char ch{};
std::字符串结果{};
结果:储备(计数);
bool readsome{false};//如果未读取任何内容,则将std::optional::has_值设为false。
while(ifs&&ifs.get(ch)&&count>0){
结果:追加(1,ch);
--计数;
可读性|=正确;
}
返回readsome?std::make_可选(结果):std::nullopt;

确保在
ios\u base::binary
模式下打开文件,否则不能假设
seekg
使用绝对文件偏移量(而您必须记住
读取后的
tellg
位置).@dxiv这样做会不会破坏以文本形式读取文件的目的?照此,从
换行符对中读取二进制副本
,并导致输出
。从表面上看,这看起来像是在复制换行符。我想我可以在
pos
,但我担心这会很慢,并且在从文件中间开始时会导致问题。
以二进制模式从
读取中以二进制副本的形式读取磁盘上的内容。如果您获得了
副本,则在读取原始数据后,这种情况会发生在其他地方。另一点是,如果您使用It在文本模式下,您只能
seekg
tellg
返回的位置,而不是基于字节计数。@dxiv我认为我的解决方案会起作用。有什么建议吗?@dxiv最后给出了标题和问题,我想它可以归结为:“我在试图保留空白时犯了一个错误。我用错了吗?”回答:“是的,你做错了。你应该这样做。”