提问人:Leon 提问时间:5/23/2023 最后编辑:Remy LebeauLeon 更新时间:5/23/2023 访问量:77
std::fstream 中的输入位置问题
Problem with input position in std::fstream
问:
使用 ,在调用函数后,我调用它的工作是将输入位置前进 1,就像在所有其他情况下一样,但这次它什么都不做,也没有前进输入位置。如下代码所示:std::fstream
testFile.getline()
testFile.seekg(1, std::ios_base::cur);
std::fstream testFile;
testFile.open("test.txt", std::ios_base::in | std::ios_base::out | std::ios_base::trunc);
if (testFile.is_open())
{
testFile.flush();
testFile.seekp(0);
char msg[]{ "GETTING IT TO WORK;\nNEXT LINE;\n" };
testFile.write(msg, 32);
testFile.put('\0');
testFile.flush();
std::string header;
header.reserve(100);
testFile.seekg(0);
testFile.getline(&header[0], 100, ';');
header.clear();
//testFile.seekg(1, std::ios_base::cur);
//uncommenting this code will do nothing only if getline has not been called
//immediately before it, will do the advancing
testFile.getline(&header[0], 100, ';');
}
在此方案中,呼叫只会将输入位置前进 1,但在之前没有呼叫时,呼叫会将输入位置前进 2。testFile.seekg(2, std::ios_base::cur)
getline()
我已经阅读了文档并亲自测试了每个场景,但文档中没有解释这种行为,也不是程序员的预期行为。
使用 MSVC v143 和 ISO C++20。
答:
突出的事实似乎是:
- 将它提高一个似乎没有区别;
- 将它前进 2 看起来像是将它前进 1;和
- 这紧跟在 A 之后,否则不会导致问题。
getline()
这似乎表明您可能有一个实际行结尾的文件,而不是 .我会使用十六进制转储实用程序检查文件的内容,看看是否是这种情况。CR/LF
\r\n
\n
Seek 实际上可能处理的是二进制内容而不是文本内容,因此跳过一个字符只会跳过 .如果是这种情况,那么您可以通过将字符读入垃圾缓冲区来修复它,而不是尝试向前寻找一个字节。前者应该有望正确处理行尾。换言之,改变:\r
testFile.seekg(1, std::ios_base::cur);
到:
int junk = testFile.get();
事实上,这似乎就是正在发生的事情。考虑以下程序,它的功能与您的代码大致相同,但依次查找每个位置并读取该行,打印它读取的第一个字符:
#include <iostream>
#include <fstream>
int main()
{
std::fstream testFile;
testFile.open("test.txt",
std::ios_base::in | std::ios_base::out | std::ios_base::trunc);
if (testFile.is_open())
{
char msg[] = "a\nb\nc;";
testFile.write(msg, sizeof(msg));
testFile.flush();
char buff[10];
for (int i = 0; i < 7; ++i) {
testFile.seekg(i);
testFile.getline(buff, 100, ';');
std::cout << "<" << int(buff[0]) << " "
<< ((buff[0] >= ' ') ? buff[0] : '.')
<< "> <"
<< int(buff[1]) << " "
<< ((buff[1] >= ' ') ? buff[1] : '.')
<< ">\n";
}
}
}
其输出为:
<97 a> <10 .>
<10 .> <98 b> *1
<10 .> <98 b> *2
<98 b> <10 .>
<10 .> <99 c> *1
<10 .> <99 c> *2
<99 c> <0 .>
您可以看到,当您寻求 或 时,字符读取的结果是 (decimal;10 是换行符)。我已经用 和 标记了发生这种情况的行。\r
\n
\n
*1
*2
因此,文本文件的基础 I/O 例程似乎将执行如下操作:
- 如果接下来的两个字符是 ,你会得到一个 ()。
\r\n
\n
*1
- 如果下一个字符是 ,你会得到一个 ()。
\n
\n
*2
这就是为什么当下一个字符是 .它正在寻求,但行为如上所述(寻求然后按照)。\r
\n
*2
顺便说一句,一个好的调试选项是在每个文件操作后输出,只是为了看看它认为它在哪里。testFile.tellg()
有趣的是,当我这样做时(在 Linux 下),缓冲区似乎移动正常,但字符串打印为空。如果我替换(并进行其他调整以匹配),似乎没问题。请注意,一个人的 seek ahead 在这里有效,可能是因为它是一个换行结束平台。string header
char header[200]
#include <iostream>
#include <fstream>
using namespace std;
int main() {
std::fstream testFile;
testFile.open("test.txt", std::ios_base::in | std::ios_base::out | std::ios_base::trunc);
if (testFile.is_open()) {
cout << testFile.tellg() << " a\n";
testFile.flush();
cout << testFile.tellg() << " b\n";
testFile.seekp(0);
cout << testFile.tellg() << " c\n";
char msg[]{ "GETTING IT TO WORK;\nNEXT LINE;\n" };
testFile.write(msg, 32);
cout << testFile.tellg() << " d\n";
testFile.put('\0');
cout << testFile.tellg() << " e\n";
testFile.flush();
cout << testFile.tellg() << " f\n";
char header[200];
testFile.seekg(0);
cout << testFile.tellg() << " g\n";
testFile.getline(&header[0], 100, ';');
cout << testFile.tellg() << " h\n";
cout << '<' << header << ">\n";
testFile.seekg(1, std::ios_base::cur);
testFile.getline(&header[0], 100, ';');
cout << testFile.tellg() << " i\n";
cout << '<' << header << ">\n";
}
}
这将输出:
0 a
0 b
0 c
32 d
33 e
33 f
0 g
19 h
<GETTING IT TO WORK>
30 i
<NEXT LINE>
我建议在您的平台上尝试该代码以查看它输出的内容。
直接写入字符串不起作用的原因似乎是,这并不一定能为此做好准备。我必须实际填充字符串,使其至少足够大(大小,而不仅仅是保留容量),以便直接写入有效。当然,之后我不得不调整长度,因为 C++ 字符串保存字符非常有效。reserve()
\0
这可能是因为直接写入只是将数据放入字符串区域,但不会影响字符串对象的任何其他重要属性(例如长度)。
将行读取器重构为:
void getUpTo(fstream &strm, string &str, size_t sz, char delim) {
if (str.length() < sz + 1)
str.resize(sz + 1);
strm.getline(&str[0], sz, delim);
str.resize(str.find('\0'));
}
并更改呼叫:
// from: testFile.getline(&header[0], 100, ';');
// to:
getUpTo(testFile, header, 100, ';');
让它起作用了。
这并不能立即适用于您的行尾问题,但即使您解决了这个问题,如果您似乎得到空/坏字符串,您也应该记住这一点。
评论
;
\n
fstream
\r\n
\r\n
\n
fstream
\r\n
评论