gpt4 book ai didi

c++ - 将文本插入文件只能执行一次

转载 作者:搜寻专家 更新时间:2023-10-30 20:27:05 25 4
gpt4 key购买 nike

我的目标:

我正在尝试用 C++ 编写一个应用程序,用户可以在其中请求特定日期范围内的特定天气参数,该程序将从互联网上找到该信息并将其写入文本文件。因此,用户可以请求 2009 年 8 月 2 日至 2009 年 8 月 10 日之间每一天的高温。然后应用程序将吐出如下文本文件:

Month,    Date,    Year,    High
8 2 2009 80.3
8 3 2009 76.9
...
8 10 2009 68.4

我已经获取了网页,将 HTML 解析为有意义的值,并将这些值写入数据库(txt 文件)。我也写了一个函数

insert(std::iostream& database, Day day); //Day is a class I defined that contains all the weather information

那会找到这一天属于待定的地方,插到中间。我已经测试了这个功能,它的工作原理和它应该的完全一样。

我的问题:

我现在正在尝试编写一个函数来执行此操作:

void updateDatabase(std::iostream& database, Day start, Day end)
{
Day currentDay = start;
while (currentDay.comesBefore(end))
{
if (currentDay.notInDatabase(database))
insert(database, currentDay);
currentDay = currentDay.nextDay();
}
}

但不幸的是,insert() 函数只有在我为每个程序调用一次时才能正常工作。如果我尝试连续调用 insert() 两次(或三次、四次或五次),只有最后一天会显示在我的文本文件中。

这是重现我的问题但仍然运行的尽可能少的代码。

#include <iostream>
#include <fstream>
#include <string>

const std::string FOLDER = "/Users/Jimmy/Desktop/WeatherApp/";
const std::string DATABASE_NAME = FOLDER + "database.txt";

class day
{
public:
int date;
int month;
int year;
bool comesBefore(int month, int date, int year);
day(int month, int date, int year)
{
this->month = month;
this->date = date;
this->year = year;
}
};

void writeToDatabase(std::iostream& file, day today, bool end = true);
void insertDay(std::iostream& file, day today);

int main()
{
std::fstream database;

database.open(DATABASE_NAME);
if (database.fail())
{
std::cout << "Cannot find database.\n";
exit(1);
}

day second(1, 2, 2000);
insertDay(database, second);

std::cout << "First day inserted. Press enter to insert second day.\n";
std::cin.get();

day third(1, 3, 2000);
insertDay(database, third);

std::cout << "Done!\n";
return 0;
}

bool day::comesBefore(int month, int day, int year)
{
if (this->year < year)
return true;
if (this->year > year)
return false;
//We can assume this->year == year.
if (this->month < month)
return true;
if (this->month > month)
return false;
//We can also assume this->month == month
return (this->date < day);
}

void writeToDatabase(std::iostream& file, day today, bool end)
{
if (end) //Are we writing at the current cursor position or the end of the file?
file.seekg(0, std::ios::end);
file << today.month << '\t' << today.date << '\t' << today.year << '\n';
return;
}

void insertDay(std::iostream& file, day today)
{
//Clear flags, and set cursor at beggining
file.clear();
file.seekg(0, std::ios::beg);

int date, month, year;
long long positionToInsert = 0;

while (!file.eof())
{
file >> month >> date >> year;
//std::cout << month << date << year << '\n';
if (today.comesBefore(month, date, year))
{
//We found the first day that comes after the day we are inserting
//Now read backwards until we hit a newline character
file.unget();
char c = '\0';
while (c != '\n')
{
file.unget();
c = file.get();
file.unget();
}
positionToInsert = file.tellg();
break;
}
}

if (file.eof())
{
//We hit the end of the file. The day we are inserting is after every day we have. Write at the end.
file.clear();
writeToDatabase(file, today);
return;
}

file.clear();
file.seekg(0, std::ios::beg);
std::fstream tempFile;
std::string tempFileName = FOLDER + "tempfile.txt";
std::string terminalCommand = "> " + tempFileName;

//Send the command "> /Users/Jimmy/Desktop/WeatherApp/tempfile.txt" to the terminal.
//This will empty the file if it exists, and create it if it does not.
system(terminalCommand.c_str());

tempFile.open(tempFileName);
if (tempFile.fail())
{
std::cout << "Failure!\n";
exit(1);
}

int cursorPos = 0;
while (cursorPos++ < positionToInsert)
{
char c = file.get();
tempFile.put(c);
}
tempFile.put('\n'); //To keep the alignment right.

writeToDatabase(tempFile, today, false);
file.get();

char c = file.get();
while (!file.eof())
{
tempFile.put(c);
c = file.get();
}

terminalCommand = "mv " + tempFileName + " " + DATABASE_NAME;
//Sends the command "mv <tempFileName> <databaseName>" to the terminal.
//This command will move the contents of the first file (tempfile) into the second file (database)
//and then delete the old first file (tempfile)
system(terminalCommand.c_str());


return;
}

我在 main 中添加了 cin.get() 部分,这样我就可以在每次 insert() 调用之前和之后查看我的数据库。这是编译/运行前的数据库:

1   1   2000
1 4 2000

这是在点击回车/继续过去 cin.get() 之前的数据库:

1   1   2000
1 2 2000
1 4 2000

这是我继续执行 cin.get() 并且我的程序退出后的数据库:

1   1   2000
1 3 2000
1 4 2000

在运行程序之前,我已经更改了插入的日期、插入的日期数、两个日期相隔多远以及数据库的初始大小,但我总是得到相同的结果。在每次调用 insert() 之后,数据库就好像这是唯一一次调用 insert 一样。但是,如果我多次运行该程序,文本文件会继续增长。如果我尝试在每次编译/运行时多次调用插入,我只会遇到这个问题。所以如果我运行这个程序 5 次:

int main()
{
std::fstream database;

database.open(DATABASE_NAME);
if (database.fail())
{
std::cout << "Cannot find database.\n";
exit(1);
}

day today(1, 2, 2000);
insertDay(database, today);

std::cout << "Done!\n";
return 0;
}

我的数据库最终看起来像这样:

1   1   2000
1 2 2000
1 2 2000
1 2 2000
1 2 2000
1 2 2000
1 4 2000

我怀疑这是 fstream.clear()、fstream.seekg() 和 fstream.eof() 的问题,或者可能与关闭/重新打开文件有关。但我为修复它所做的一切都没有帮助。

此外,值得注意的是,这不会在 Windows 计算机上运行。在 linux 上应该没问题,但我只在 Mac 上测试过,所以我可能是错的。它使用 bash 来创建/删除/重命名/移动文件。

我们非常感谢您提供任何帮助(即使只是朝正确方向插入)。一段时间以来,我一直在为这个问题烦恼。另外,我知道 SO 不喜欢代码转储,所以我大大简化了这个问题。我的完整程序有 700 多行和 10 个不同的文件,这是我在理解想法的同时尽可能短的。

最佳答案

这里的问题与您处理文件的方式有关:当您mv 一个文件时,旧文件本身不会被覆盖;相反,它是未链接(“已删除”),并在原处创建了一个新文件。

在类 Unix 操作系统上,您仍然可以保留未链接文件的句柄:只是无法使用路径访问它。这就是为什么在 Unix 上完全可以删除仍然打开的文件,这与在 Windows 上不同:the file still exists after you have unlinked it, at least until all the file descriptors have been closed .这意味着数据库 根本没有改变:它仍然指向您的旧文件并包含相同的内容。

一个简单的解决方法是关闭并重新打开该文件。 (从实用的角度来看,使用现成的解决方案(例如 Sqlite 可能要好得多。)

关于c++ - 将文本插入文件只能执行一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27577467/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com