So, I'm coding for a C++ program (well, actually a .dll plugin for RpgMaker 2003), and I have a code like this:
#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, plus storing text
{
FileName = parsedData->parameters[0].text;
ofstream myfile;
myfile.open (FileName.c_str(), 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,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;
}
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;
}
Generally, this is how the .dll plugin works: It runs the DynRPG headers through the linker settings, and the RPG_RT.exe of RpgMaker as its host arguments. onStartup and onComment are DynRPG-specific codes to run compatibly with RpgMaker. You don't need to know most of this, except to know that these if(!cmd.compare(" ")) codes basically are commands for RpgMaker. So, I am telling various files to open text files, but I've been having problems:
I have a code to clear file, and it does clear the file. But I think
it creates the file in order to clear it, rather than skipping over
if it doesn't exist. In fact, I think this is a persistent issue, I
dunno how to get the code to skip over nonexistent file names, so it
makes new files if the code checks files.
If possible, that clear file should actually delete the files rather
than just making it blank.
It appends information into the file I make (and there doesn't seem
to be any memory leak), but it doesn't check to make sure that I
haven't already entered the same word or phrase. When I'm writing
into the file, I want to check But I think I only know how to check
output when loading output, not before input. I want to avoid
duplicates. Currently, I have a file called "awards.txt" (unlockable
awards) and I have the file with like 6 instances of the same word.
I want it to write the same code only once. This is my main problem.
Don't give obtuse advice, assuming I will understand what you mean. Show me actual code. I barely pieced together this much code from reading and adapting other tutorials, and I do not have a degree in computer science (I was a history major, so the theory of C++ code doesn't always make sense to me). Oh and uhhhh, not all the header files are necessary, I tend to include more than I need just in case.
Point 1
In
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;
}
the default file open mode used in myfile.open (FileName.c_str()); will create an empty file, overwriting an exiting file. Cool. Empty file is what you wanted, but yeah, it created an empty file where none existed. To avoid that, you'd need to specify an open mode that won't create a new file. Using this groovy table here, specifying the in open mode will raise an error if the file doesn't exit rather than opening it. Unfortunately if you keep reading you'll find there is no combination of openmodes that will truncate (clear) a file without also creating a new file.
Well doesn't that suck?
In C++17 we can use the <filesystem> Library's resize_file function.
std::error_code code;
std::filesystem::resize_file(FileName, 0, code);
// test code here if you care. Someone else could have the file open and prevent clearing.
Before C++17, its easiest to use system-specific API calls. You mention DLL above, so I'm going to assume a Windows target
HANDLE h = CreateFileA(FileName.c_str(),
GENERIC_WRITE,
0,
NULL,
TRUNCATE_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (h != INVALID_HANDLE_VALUE)
{
CloseHandle(h);
}
Documentation on CreateFileA. Note I use CreateFileA and not CreateFileW or CreateFile because FileName doesn't contain wide characters.
There might be a more direct call. I'm not really that familiar with the Win132 API.
A POSIX compliant target like Linux (Maybe Mac. I know even less about Macs) 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.
If you're lucky the compiler developers ported truncate to Windows for you and simplify your job because you only have to support one target.
Side note
Because the openmode was what's actually clearing the file, we should take a look at what myfile.clear(); does: clears error flags on a stream. Normally when a stream operation fails error flags are set so you can check to see what went wrong. When any error flags are set, the stream refuses to process any further transactions and instantly returns. It's on you-the-programmer to check the state of the stream after every transaction to ensure that it worked because if it didn't and you don't check, you'll never know. The stream state and error flags are the ONLY warning you get.
So, you check the flags, handle the flagged error, and then call clear to signal you've resolved the issue and are ready to get back to reading and writing. Note: Sometimes handling the error requires you to clear the flag early in order to remove, read out, bad data so that the program doesn't immediately find the same garbage and fail again once you return to the regular flow of the code.
Point 2
Deleting a file... Now that's easier. In 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!
And in the old world,
if (remove(FileName.c_str()) !=)
{
//check errno to find out why
}
Documentation for std::filesystem::remove
Documentation for remove
Point 3
I just saw the time and I need to stop. You should remove point three from the question since it is a completely different issue and deserves its own question.
Related
first time here. I am a student doing some c++ coding for end year project. The programme that I coded does not read the text file even though everything seems to be in order. Some helps would be fantastic!
void transactionRecords(double total, char answer, string nameT, int HpNo, string address)
{
fstream myFile;
string name;
char idStatus;
double amt, sumAll=0;
myFile.open("transaction.txt",fstream::in);
if (!myFile) cout<<"Unable to Open File under Input Mode";
else
{
while (!myFile.eof())
{
myFile>>name>>idStatus>>address>>HpNo>>amt;
if (myFile.fail()) break;
}
myFile.close();
myFile.open("transaction.txt",fstream::app);
if (!myFile) cout<<"Unable to Open File under App Mode";
else
{
myFile<<nameT<<" "<<answer<<" "<<address<<" "<<HpNo<<" "<<total<<endl;
if (myFile.fail()) cout<<"Error encountered while adding data!\n";
}
}
myFile.close();
}
this is whats in the text file
Johns Y pasir_ris 81231211 4.14
First, I suggest you use the RAII principle when creating files that is:
void myfunction() {
ifstream f{"file.txt"};
// your logic here
// NB -- no need to manually close file, prevents resource leaks
}
There is no need to manually close or open such a file, it is opened by the constructor and closed when the destructor is invoked upon exiting the current scope. This prevents leaks of file handles and is a pervasive technique in C++.
Second, use the standard stream read loop in C++:
while (myFile>>name>>idStatus>>address>>HpNo>>amt) {
// your logic here, the read has succeded
// TODO process myFile, name, idStatus etc.
}
With these changes, your example should look something like:
void transactionRecords(double total, char answer, string nameT, int HpNo, string address)
{
ifstream myFile{"transaction.txt"};
string name;
char idStatus;
double amt, sumAll=0;
if (!myFile) cout<<"Unable to Open File under Input Mode";
return;
while (myFile>>name>>idStatus>>address>>HpNo>>amt) {
// TODO do something here??
}
ofstream tFile{"transaction.txt"};
if (!myFile) cout<<"Unable to Open File under App Mode";
return;
if (!(tFile<<nameT<<" "<<answer<<" "<<address<<" "<<HpNo<<" "<<total<<endl)) {
cout<<"Error encountered while adding data!\n";
}
}
}
You should probably do something in the TODO block, currently you are only storing the last values read? If you only mean to process one line from the file, swap the while loop with an if.
I wrote a code in C++ that writes a .txt file.
Then I want to open the code again and give some information, so I can get a new text depending on what I gave as an input.
For example I want to give the name of a month, and print in another .txt file all the lines that came after the word "November".
I found some solutions, but none of them worked for me!
One solution that I found on stack overflow is the following:
void Keyword(ifstream & stream, string token) {
string line;
while (getline(stream, line)) {
if (line.find(token) != string::npos) {
cout << line << endl;
}
}
cout << token << " not found" << endl;
}
I can't print the next lines with the code above.
Any suggestion would be helpful!
Thanks!
If you want to perform operations on files such as 'Read' and/or 'Write',you might want to search on the net(or if you have a C++ book) on topics such as "File I/O operations using C++". Anyways moving on, C++ has 2 basic classes to handle files which are ifstream and ofstream. And to use them you have to include ethier the header fstream(i.e #include<fstream>) or include them separately as #include<ifstream> and #include<ofstream>. ifstream is basically used for all input operations such as reading files etc. Similarly ofstream is used for all output operations such as writing data to files.
You can open a file and write data to it by doing the following,
ofstream myFile("filename");// Create an instance of ofstream and open file for writing data
and to write data to the file use the << operator like below,
myFile<<data;
Similarly, You can open a file and read data as follows,
ifstream myFile("filename");//Create an instance of ifstream and open file to read data
and to read data from the file use the >> operator as shown below,
myFile>>data;
You can also open a file using the method void open(const char *filename, ios::openmode mode); as shown below,
//Writing only
ofstream outFile;
outFile.open("filename.txt",ios::out);
//Reading only
ifstream inFile;
inFile.open("filename.txt",ios::in);
//For reading and writing
fstream file;
file.open("filename.txt",ios::in|ios::out);
//For closing File
outFile.close();
//or
inFile.close();
//or
file.close();
Note the open() method takes various flags such as ios::in for reading mode, ios::out for writing mode, ios::app for adding data to the end etc.
All of these can also combined by using the bit OR operator | as shown below,
outFile.open("filename.txt",ios::out|ios::app);
There is a lot more in IO. I just covered the things required to start.
Here is the solution to your problem. Try to understand it.
#include<iostream>
#include<fstream>
#include<cstring>
using namespace std;
int main()
{
ofstream outFile;
ifstream inFile;
char fileName[10],data[50];
int noLines;
cout<<"Enter Month:"<<endl;
cin>>fileName;
cout<<"Enter Number of lines you want to enter:"<<endl;
cin>>noLines;
outFile.open(fileName,ios::out);
cout<<fileName<<"(Enter Data):";
for(int i=0;i<=noLines;i++)
{
cin.getline(data,50);
outFile<<data<<endl;
}
outFile.close();
cout<<"Openening "<<fileName<<" :"<<endl;
inFile.open(fileName,ios::in);
for(int i=0 ;i<=noLines ;i++)
{
inFile.getline(data,50);
cout<<data<<endl;
}
inFile.close();
return 0;
}
OP has found most of the solution already:
string line;
while (getline(stream, line)) {
if (line.find(token) != string::npos) {
cout << line << endl;
}
}
cout << token << " not found" << endl;
But this only prints the lines with the keyword. And always prints the "not found" message. Ooops.
Instead I pitch:
string line;
bool found = false;
while (!found && getline(stream, line))
{ // search for keyword
if (line.find(token) != string::npos)
{
found = true; // found keyword. Stop looking
}
}
if (found)
{ // print out all remaining lines in the file
while (getline(stream, line))
{
cout << line << endl;
}
}
else
{
cout << token << " not found" << endl;
}
The above splits the finding of the token and the printing of the remaining file into two stages for readability. It can be compressed into one loop, but two things make this a sucker bet:
this program will be IO bound. It will spend the vast majority of its time reading the file, so little tweaks that do not address getting the file into memory are wasted time.
combining the loops would require the addition of logic to the loop that would, over along run, dwarf the minuscule cost of switching loops.
Try this:
http://www.cplusplus.com/doc/tutorial/files/
and this:
http://www.cplusplus.com/forum/beginner/14975/
It's about reading and writing files in c++ and about searching in files.
I wrote a small program that opens a file, builds a vector from every line in the file and then have the user able to add/remove from the file. The program first removes from the vector, then rebuilds the file based on the vector. Here's the code that rebuilds the file (fileName is a member variable with the full name of the text file, ex. "test.txt":
bool rebuildFile() {
if (remove(fileName.c_str()) == 0) { // remove the old file
ofstream newFile(fileName); // create new file with same name
newFile.open(fileName, ios::app); // open to append to end of file
if (newFile.is_open()) {
newFile << fileHeader << endl; // add the first header line
for (int i = 0; i < myVector.size(); i++) { // loop through vector, adding strings to file
newFile << myVector[i] << endl; // I used "\n" instead of endl, but both give same results
}
newFile.close();
return true; // success
}
}
return false; // failure
}
After this function exits, the file is completely empty. So it clearly creates a new file, but then the writing part is an issue, and I can't figure out why. I read other posts where some had issues where they had the file open in Notepad/Notepad++, but I've always made sure to close that specific file before running the program. I'm not sure if the ios::app flag is causing an issue with the loop, but the documentation seems clear that it just points to the end of the file every time you output to it, so I don't think the issue is there. Any thoughts?
EDIT:
Apparently you can't append to an empty file... This new code works, but I'm not sure if there's a "cleaner" way to add to a file in two different ways without opening and closing it twice using different flags.
new code:
bool rebuildFile() {
if (remove(fileName.c_str()) == 0) {
std::ofstream newFile(fileName);
newFile.open(fileName);
if (newFile.is_open()) {
newFile << fileHeader << endl;
newFile.close();
}
newFile.open(fileName, std::ofstream::out | std::ofstream::app);
if (newFile.is_open()) {
for (int i = 0; i < myVector.size(); i++) {
newFile << myVector[i] << endl;
}
newFile.close();
return true;
}
}
return false;
}
Trying to call open on an already open file stream puts the stream in a failed state.
Just change
ofstream newFile(fileName); // create new file with same name
newFile.open(fileName, ios::app);
to
ofstream newFile(fileName, ios::app);
[ofstream.members]
void open(const char* s, ios_base::openmode mode = ios_base::out);
Effects: Calls rdbuf()->open(s, mode | ios_base::out). If that
function does not return a null pointer calls clear(), otherwise calls
setstate(failbit) (which may throw ios_base::failure (27.5.5.4)).
[filebuf.members]
basic_filebuf<charT,traits>* open(const char* s, ios_base::openmode mode);
Effects: If is_open() != false, returns a null pointer. [...]
bool is_open() const;
Returns: true if a previous call to open succeeded (returned a
non-null value) and there has been no intervening call to close.
ofstream newFile(fileName);
doesn't just create the file, it opens it too. And that means you can't open it again.
I don't see a reason to remove the file, re-create it - truncating if it exists - and open it, write a little, close the file, open it again but for appending, and then write to it some more.
Plus, if you run out of luck, there's an opportunity for a different process to modify (or delete) the file between the first close and the second open, which is in general not a good thing.
This snippet should work:
bool rebuildFile()
{
std::ofstream newFile(fileName);
if (newFile)
{
newFile << fileHeader << endl;
for (int i = 0; i < myVector.size(); i++) {
newFile << myVector[i] << endl;
}
}
return newFile;
}
(Files are closed automatically, if needed, by ofstream's destructor.)
I'm trying to write a simple program that will print the contents of a text file one line at a time. However, whenever I run the program i just get a blank screen. I'm certain the file I am trying to read contains text over several lines. Any help as to why this isn't working would be super helpfull.
bool show() {
string line;
ifstream myfile;
myfile.open("tasks.txt", ios::app);
while (!myfile.eof()) {
getline (myfile, line);
cout << line << endl;
}
myfile.close();
return true;
}
The problem might be that you are using ios::app with ifstream (input stream), which makes no sense.
According to this,
ios::app: All output operations are performed at the end of the file, appending the content to the current content of the file. This flag can only be used in streams open for output-only operations.
Try this:
std::string line;
ifstream myfile ("tasks.txt");
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
std::cout << line << std::endl;
}
myfile.close();
}
Did you check return value of myfile.isopen()? Perhaps the file isn't there or you don't have read permission.
Oh yes, I missed that - the append flag. Should be ios::in
If I include the if test in my code the error message is returned and I'm not sure why.
and when it's not used, my program get's stuck in a loop where it never reaches the end of the file. I don't understand what's going wrong.
int countlines()
{
fstream myfile;
myfile.open("questions.txt", ios::in);
string contents;
int linenumber = 0;
//if (myfile.is_open())
// {
while (!myfile.eof())
{
getline( myfile, contents );
if (contents != "")
{
linenumber++;
}
}
cout << "there are " << linenumber << " lines.\n";
//}else {cout<<"Unable to get file.\n";}
myfile.close();
return(linenumber);
}
What's going on is that your file is not being opened. That's why is_open fails.
Then, when you comment out the check, you're breaking your loop because you're iterating incorrectly (see my comment) and not detecting stream failures (.eof() will never be true on that stream).
Make sure that the file is in the right place, and that it is accessible.
The correct idiom for reading a file line-by-line in C++ is using a loop like this:
for (std::string line; std::getline(file,line);)
{
// process line.
}
Inserting this in your example (+fixing indentation and variable names) gives something like this:
int countlines(const std::string& path)
{
// Open the file.
std::ifstream file(path.c_str());
if (!file.is_open()) {
return -1; // or better, throw exception.
}
// Count the lines.
int count = 0;
for (std::string line; std::getline(file,line);)
{
if (!line.empty()) {
++count;
}
}
return count;
}
Note that if you don't intend to process the line contents, you can actually skip processing them using std::streambuf_iterator, which can make your code look like:
int countlines(const std::string& path)
{
// Open the file.
std::ifstream file(path.c_str());
if (!file.is_open()) {
return -1; // or better, throw exception.
}
// Refer to the beginning and end of the file with
// iterators that process the file character by character.
std::istreambuf_iterator<char> current(file);
const std::istreambuf_iterator<char> end;
// Count the number of newline characters.
return std::count(current, end, '\n');
}
The second version will completely bypass copying the file contents and avoid allocating large chunks of memory for long lines.
When using std::istream and std::ostream (whose std::fstream implements), the recommended usage is to directly use the stream in a bool context instead of calling eof() function because it only return true when you managed to read until the last byte of the file. If there was any error before that, the function will still return true.
So, you should have written your code as:
int countlines() {
ifstream myfile;
int linenumber = 0;
string linecontent;
myfile.open("question.txt", ios::in);
while (getline(myfile, linecontent)) {
if (!linecontent.empty()) {
++linenumber;
}
}
return linenumber;
}
Try the following code. It will also (hopefully) give you an idea why the file open is failing...
int countlines()
{
ifstream myfile;
myfile.open("questions.txt");
string contents;
int linenumber = 0;
if (myfile.is_open())
{
while (getline(myfile, contents))
{
if (contents != "")
linenumber++;
}
cout << "there are " << linenumber << " lines." << endl;
myfile.close();
}
else
cout << "Unable to get file (reason: " << strerror(errno) << ")." << endl;
return linenumber;
}