在尝试以文本模式(Windows)读取简单的ANSI编码文本文件时,我遇到了一些奇怪的行为, seekg()和 tellg() ;任何时候我尝试使用 tellg(),保存它的值(作为pos_type),然后在以后查找它,我总是会在流中比我离开的地方更进一步。
最后我做了一次健全检查;即使我这样做......
int main()
{
std::ifstream dataFile("myfile.txt",
std::ifstream::in);
if (dataFile.is_open() && !dataFile.fail())
{
while (dataFile.good())
{
std::string line;
dataFile.seekg(dataFile.tellg());
std::getline(dataFile, line);
}
}
}
...然后最终,进一步进入文件,行被截止一半。为什么会发生这种情况?
答案 0 :(得分:4)
此问题是由libstdc ++使用当前剩余缓冲区与lseek64
之间的差异来确定当前偏移量引起的。
使用read
的返回值设置缓冲区,对于Windows上的文本模式文件,它返回在结束转换后放入缓冲区的字节数(即2字节{{1} } endline被转换为\r\n
,windows似乎也会在文件的末尾附加一个虚假的换行符。
\n
但是(mingw导致调用lseek64
)会返回当前的绝对文件位置,一旦减去这两个值,你最终会得到一个偏离1的偏移量文本文件中的每个剩余换行符(额外换行符为+1)。
以下代码应显示问题,您甚至可以使用具有单个字符且没有换行符的文件,因为Windows会插入额外的换行符。
_lseeki64
对于具有单个#include <iostream>
#include <fstream>
int main()
{
std::ifstream f("myfile.txt");
for (char c; f.get(c);)
std::cout << f.tellg() << ' ';
}
字符的文件,我得到以下输出
a
第一次拨打2 3
时,显然关闭1。在第二次调用之后,文件位置是正确的,因为考虑了额外的换行符后已达到结束。
除了以二进制模式打开文件外,您还可以通过禁用缓冲来解决问题
tellg
但这远非理想。
希望mingw / mingw-w64或gcc可以解决这个问题,但首先我们需要确定谁负责修复它。我认为基本问题在于lsek的MS实现,它应该根据文件的打开方式返回适当的值。
答案 1 :(得分:0)
Thanks for this , though it's a very old post. I was stuck on this problem for more then a week. Here's some code examples on my site (the menu versions 1 and 2). Version 1 uses the solution presented here, in case anyone wants to see it .
:)
void customerOrder::deleteOrder(char* argv[]){
std::fstream newinFile,newoutFile;
newinFile.rdbuf()->pubsetbuf(nullptr, 0);
newinFile.open(argv[1],std::ios_base::in);
if(!(newinFile.is_open())){
throw "Could not open file to read customer order. ";
}
newoutFile.open("outfile.txt",std::ios_base::out);
if(!(newoutFile.is_open())){
throw "Could not open file to write customer order. ";
}
newoutFile.seekp(0,std::ios::beg);
std::string line;
int skiplinesCount = 2;
if(beginOffset != 0){
//write file from zero to beginoffset and from endoffset to eof If to delete is non-zero
//or write file from zero to beginoffset if to delete is non-zero and last record
newinFile.seekg (0,std::ios::beg);
// if primarykey < largestkey , it's a middle record
customerOrder order;
long tempOffset(0);
int largestKey = order.largestKey(argv);
if(primaryKey < largestKey) {
//stops right before "current..." next record.
while(tempOffset < beginOffset){
std::getline(newinFile,line);
newoutFile << line << std::endl;
tempOffset = newinFile.tellg();
}
newinFile.seekg(endOffset);
//skip two lines between records.
for(int i=0; i<skiplinesCount;++i) {
std::getline(newinFile,line);
}
while( std::getline(newinFile,line) ) {
newoutFile << line << std::endl;
}
} else if (primaryKey == largestKey){
//its the last record.
//write from zero to beginoffset.
while((tempOffset < beginOffset) && (std::getline(newinFile,line)) ) {
newoutFile << line << std::endl;
tempOffset = newinFile.tellg();
}
} else {
throw "Error in delete key"
}
} else {
//its the first record.
//write file from endoffset to eof
//works with endOffset - 4 (but why??)
newinFile.seekg (endOffset);
//skip two lines between records.
for(int i=0; i<skiplinesCount;++i) {
std::getline(newinFile,line);
}
while(std::getline(newinFile,line)) {
newoutFile << line << std::endl;
}
}
newoutFile.close();
newinFile.close();
}
beginOffset is a specific point in the file (beginning of each record) , and endOffset is the end of the record, calculated in another function with tellg (findFoodOrder) I did not add this as it may become very lengthy, but you can find it on my site (under: menu version 1 link) :