问题描述
所以,我正在编写一个 C++ 程序(嗯,实际上是 Rpgmaker 2003 的 .dll 插件),我有这样的代码:
#include <DynRPG/DynRPG.h>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
std::map<std::string,std::string> configuration;
bool onStartup(char *pluginName) {
// We load the configuration from the DynRPG.ini file here
configuration = RPG::loadConfiguration(pluginName);
return true; // Don't forget to return true so that the start of the game will continue!
}
bool onComment( const char* text,const RPG::ParsedCommentData* parsedData,RPG::EventScriptLine* nextScriptLine,RPG::EventScriptData* scriptData,int eventId,int pageId,int lineId,int* nextLineId )
{
std::string cmd = parsedData->command;
std::string filecode;
std::string scanfile;
string FileName;
if(!cmd.compare("file_string")) //new code
{
ofstream myfile;
myfile.open ("dynstore.txt",ios::app);
myfile << parsedData->parameters[0].text << "\n";
myfile.close();
return false;
}
if(!cmd.compare("store_file")) //new code
{
ofstream myfile;
myfile.open ("dynstore.txt",ios::app);
myfile << RPG::actors[parsedData->parameters[0].number]->name << "\n";
myfile.close();
return false;
}
if(!cmd.compare("load_file")) //new code
{
ifstream myfile;
myfile.open ("dynstore.txt");
if(myfile.good()) //this should skip if the file doesn't exist.
{
getline (myfile,filecode);
RPG::actors[parsedData->parameters[0].number]->name = filecode;
}
myfile.close();
return false;
}
//These first three allow only storage using dynstore.
if(!cmd.compare("create_file")) //custom file name,plus storing text
{
FileName = parsedData->parameters[0].text;
ofstream myfile;
myfile.open (FileName.c_str(),ios::app);
myfile << parsedData->parameters[1].text << "\n";
myfile.close();
return false;
}
//This stores a word and makes a new line
if(!cmd.compare("create_save")) //custom file name,plus storing text
{
int GameFile = RPG::variables[3351];
ofstream myfile;
if(GameFile == 0)
{
FileName = parsedData->parameters[0].text;
myfile.open (FileName.c_str(),ios::app);
}
if(GameFile == 1)
{
myfile.open ("Save01.txt",ios::app);
}
if(GameFile == 2)
{
myfile.open ("Save02.txt",ios::app);
}
if(GameFile == 3)
{
myfile.open ("Save03.txt",ios::app);
}
if(GameFile == 4)
{
myfile.open ("Save04.txt",ios::app);
}
if(GameFile == 5)
{
myfile.open ("Save05.txt",ios::app);
}
if(GameFile == 6)
{
myfile.open ("Save06.txt",ios::app);
}
myfile << parsedData->parameters[1].text << "\n";
myfile.close();
return false;
}
//This stores a word and makes a new line
if(!cmd.compare("make_file")) //custom file name,ios::app);
myfile << RPG::actors[parsedData->parameters[1].number]->name;
myfile.close();
return false;
}
//This stores a name
if(!cmd.compare("read_file")) //sets data info to name for easy read
{
FileName = parsedData->parameters[0].text;
ifstream myfile;
myfile.open (FileName.c_str());
if(myfile.good()) //this should skip if the file doesn't exist.
{
getline (myfile,filecode);
RPG::actors[parsedData->parameters[1].number]->name = filecode;
}
myfile.close();
return false;
}
if(!cmd.compare("clear_file")) //sets data info to name for easy read
{
FileName = parsedData->parameters[0].text;
ofstream myfile;
myfile.open (FileName.c_str());
myfile.clear();
myfile.close();
return false;
}
if(!cmd.compare("scan_save"))
{
//Attempt ONE to scan the file for a string
//this one works by turning on a switch if the string is found
int GameFile = RPG::variables[3351];
ifstream myfile;
if(GameFile == 0)
{
FileName = parsedData->parameters[0].text;
myfile.open (FileName.c_str());
}
if(GameFile == 1)
{
myfile.open ("Save01.txt");
}
if(GameFile == 2)
{
myfile.open ("Save02.txt");
}
if(GameFile == 3)
{
myfile.open ("Save03.txt");
}
if(GameFile == 4)
{
myfile.open ("Save04.txt");
}
if(GameFile == 5)
{
myfile.open ("Save05.txt");
}
if(GameFile == 6)
{
myfile.open ("Save06.txt");
}
scanfile = parsedData->parameters[1].text;
int switchnum = parsedData->parameters[2].number;
if(myfile.good()) //this should skip if the file doesn't exist.
{
for(std::string temp;!myfile.eof();std::getline(myfile,temp))
{
if(temp.find(scanfile)!=std::string::npos)
{
RPG::switches[switchnum] = true;
//Turns on the switch if the string has been found in the file
//ideally,I'd want to save a new name using the scanfile,but I can't figure out how to copy it
}
}
}
myfile.close();
return false;
} //This checks only the current save,defaulting to the input if none exists
if(!cmd.compare("scan_file"))
{
//Attempt ONE to scan the file for a string
//this one works by turning on a switch if the string is found
FileName = parsedData->parameters[0].text;
ifstream myfile;
myfile.open (FileName.c_str());
scanfile = parsedData->parameters[1].text;
int switchnum = parsedData->parameters[2].number;
if(myfile.good()) //this should skip if the file doesn't exist.
{
for(std::string temp;!myfile.eof();std::getline(myfile,but I can't figure out how to copy it
}
}
}
myfile.close();
return false;
}
if(!cmd.compare("file_good"))
{
//Checks for the existence of a file.
FileName = parsedData->parameters[0].text;
ifstream myfile;
myfile.open (FileName.c_str());
int switchnum = parsedData->parameters[1].number;
if(myfile.good()) //this should skip if the file doesn't exist.
{
RPG::switches[switchnum] = true;
}
else
{
RPG::switches[switchnum] = false;
}
myfile.close();
return false;
}
//End of cmd
return true;
}
通常,.dll 插件是这样工作的:它通过链接器设置运行 DynRPG 标头,并将 Rpgmaker 的 RPG_RT.exe 作为其主机参数。 onStartup 和 onComment 是 DynRPG 特定的代码,可与 Rpgmaker 兼容运行。你不需要知道大部分,除了知道这些 if(!cmd.compare(" ")) 代码基本上是 Rpgmaker 的命令。所以,我告诉各种文件打开文本文件,但我一直遇到问题:
- 我有一个清除文件的代码,它确实清除了文件。但我认为 它创建文件是为了清除它,而不是跳过 如果它不存在。事实上,我认为这是一个长期存在的问题,我 不知道如何让代码跳过不存在的文件名,所以它 如果代码检查文件,则会生成新文件。
- 如果可能,该清除文件实际上应该删除文件而不是 而不仅仅是让它成为空白。
- 它将信息附加到我制作的文件中(似乎没有 是任何内存泄漏),但它不会检查以确保我 尚未输入相同的单词或短语。当我写作 进入文件,我想检查 但我想我只知道如何检查 加载输出时输出,而不是在输入之前。我想避免 重复。目前,我有一个名为“awards.txt”的文件(可解锁 奖),我有同一个词的 6 个实例的文件。 我希望它只编写一次相同的代码。这是我的主要问题。
不要给出愚蠢的建议,假设我会理解你的意思。给我看看实际代码。我几乎没有通过阅读和改编其他教程拼凑这么多代码,而且我没有计算机科学学位(我是历史专业的,所以 C++ 代码理论对我来说并不总是有意义)。哦,呃,并不是所有的头文件都是必需的,我倾向于包含比我需要的更多以防万一。
解决方法
第 1 点
在
if(!cmd.compare("clear_file")) //sets data info to name for easy read
{
FileName = parsedData->parameters[0].text;
ofstream myfile;
myfile.open (FileName.c_str());
myfile.clear();
myfile.close();
return false;
}
myfile.open (FileName.c_str());
中使用的默认文件打开模式将创建一个空文件,覆盖现有文件。凉爽的。空文件是您想要的,但是,它创建了一个不存在的空文件。为避免这种情况,您需要指定一种不会创建新文件的打开模式。 Using this groovy table here,如果文件没有退出而不是打开它,则指定 in
打开模式将引发错误。不幸的是,如果您继续阅读,您会发现没有任何 openmodes 组合可以在不创建新文件的情况下截断(清除)文件。
那是不是很糟糕?
在 C++17 中,我们可以使用 <filesystem>
Library's resize_file
function。
std::error_code code;
std::filesystem::resize_file(FileName,code);
// test code here if you care. Someone else could have the file open and prevent clearing.
在 C++17 之前,最容易使用系统特定的 API 调用。你在上面提到了 DLL,所以我将假设一个 Windows 目标
HANDLE h = CreateFileA(FileName.c_str(),GENERIC_WRITE,NULL,TRUNCATE_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (h != INVALID_HANDLE_VALUE)
{
CloseHandle(h);
}
Documentation on CreateFileA
。请注意,我使用 CreateFileA
而不是 CreateFileW
或 CreateFile
,因为 FileName
不包含宽字符。
可能会有更直接的调用。我对 Win132 API 不太熟悉。
像 Linux 这样的 POSIX 兼容目标(也许是 Mac。我对 Mac 的了解更少)uses truncate
int code = truncate(FileName.c_str(),0);
// test code here and look at errno if you care. Someone else could have the file open.
如果幸运的话,编译器开发人员会为您将 truncate
移植到 Windows 并简化您的工作,因为您只需要支持一个目标。
附注
因为 openmode 实际是在清除文件,所以我们应该看看 myfile.clear();
做了什么:清除流上的错误标志。通常,当流操作失败时,会设置错误标志,以便您可以检查哪里出了问题。当设置了任何错误标志时,流拒绝处理任何进一步的事务并立即返回。在每个事务之后检查流的状态以确保它工作是你的程序员,因为如果它没有并且你不检查,你永远不会知道。流状态和错误标志是您收到的唯一警告。
因此,您检查标志,处理标记的错误,然后调用 clear
以表示您已解决问题并准备恢复读写。注意:有时处理错误需要您尽早清除标志以删除、读出错误数据,以便程序不会立即找到相同的垃圾并在您返回到代码的常规流程时再次失败。
第 2 点
删除文件...现在更容易了。在 C++17 中
std::error_code code;
std::filesystem::remove("test",code);
// Wrap in an if and test code if you care about failure. I would.
// If it's file not found,cool!
在旧世界,
if (remove(FileName.c_str()) !=)
{
//check errno to find out why
}
Documentation for std::filesystem::remove
第 3 点
我刚看到时间,我需要停下来。您应该从问题中删除第三点,因为这是一个完全不同的问题,值得提出自己的问题。