Preventing line repeats in files in C++ - c++

I apologize before-hand if this question was already asked and my google skills failed me.
I'm making a simple console game with ncurses and I wanted to include this locked zip folder with extra lore, bonus material, etc...
I can write the codes to the file just fine but for whatever reason when I revisit the place that wrote the text to the file it repeats itself. I've tried looking for a solution and haven't found one so this is my last resort.
basic information: I use windows but I want the program to be cross-platform. If any more information is required I'd be glad to provide it.
EDIT 1:
std::ifstream checkFile("Unlocks.txt");
if(checkFile.is_open())
{
std::string data;
std::string fernox = "Unlock for Fernox Piraxis File";
while(std::getline(checkFile, data))
{
if(data.find(fernox) == std::string::npos)
{
std::ofstream myFile("Unlocks.txt", std::ios::app);
myFile << "Unlock for Fernox Piraxis File: ZWdOFMRmeE\n";
myFile.close();
break;
}
}
checkFile.close();
}
EDIT 2:
I'm not trying to overwrite any part of the other file. This code is "supposed" to check if the line above is already written in the file and if it isn't, write it. If the line already exists within the file I don't want it to write the same line again (and I'm using ios::app so that it doesn't overwrite anything already in the file.
Thanks in advance for the help.
EDIT 3:
working now thanks to twalberg.
Final Code:
std::ifstream checkFile ("Unlocks.txt");
if(checkFile.is_open())
{
bool found = false;
std::string data;
std::string fernox ("Unlock for Fernox Piraxis File");
while(std::getline(checkFile, data))
{
if(data.find(fernox) != std::string::npos)
{
found = true;
break;
}
}
if(!found)
{
std::ofstream myFile("Unlocks.txt", std::ios::app);
myFile << "Unlock for Fernox Piraxis File: ZWdOFMRmeE\n";
myFile.close();
}
checkFile.close();
}

Your current logic is a little off. You are reading the first line of the file, and if that line doesn't match, you append the string and break out of the loop. What you need is a structure more like this, checking each line of the file, and only then deciding whether to append your string:
// open file
bool found = false;
while (std::getline(checkFile, data))
{ if (data.find(fernox) != std::string::npos) // we found a match
{ found = true;
break;
}
}
if (!found)
{ // append string here
}
// close file

Related

Checking whether file contains only whitespace C++

I am trying to read input from a file in C++, and I need to give an error message if there is no input.
This statement works if the file is completely empty:
if (f.peek() == std::ifstream::traits_type::eof()) return error("Empty file");
where error() is a simple function:
int error(string message){
cerr << "ERROR: " << message << "\n";
return -1;
}
How can I check for files that contain only whitespace so I can raise the same error? Such as 7 newlines?
My should continue executing normally if the file contains anything but only whitespace characters.
Scanning the file in advance if it only contains whitespaces would be very inefficient.
I believe what you really need, is to keep track if some non-whitespace data could be read at all, and raise the error if not:
std::string line;
std::vector<std::string> lines_with_data;
while(std::getline(f,line)) {
// check if line is empty or contains only whitespace
if(!(line.empty() ||
(std::find_if_not(line.begin(),line.end(),std::isspace) != line.end())) {
lines_with_data.push_back(line);
}
}
if(lines_with_data.empty()) { // No data could be found
return error("Empty file");
}
reading characters and checking with std::isspace may help. something like this:
char c;
bool isEmpty = true;
while (f.get(c))
{
if(!std::isspace(c))
{
isEmpty = false;
break;
}
}
this way, isEmpty will be what it says
You could use an std::istream_iterator over the file input and check individual characters with std:isspace(), I suppose.
But I wonder if this is really what you want to do. Why not try to read whatever it is you want to read, and only worry about things when you fail your reading/parsing?

Editing a text file

I want to edit a text file, but I'm stuck in finding the correct functions or methods to do so.
So far I'm able to open a text file and look for a certain string, but I have no idea on how to move the cursor, add or replace information, steps 4 - 7 in my pseudocode shown below.
Can you provide some guidance? Which functions should I use (in case they already exist)?
A sample 'easy' code would be appreciated as well.
Pseudocode:
1. Open file.
2. While not eof
3. Read file until string "someString" is found.
4. Position the cursor at the next line (to where the someString was found).
5. If "someString" = A go to step 6. Else go to step 7.
6. Replace the information in whole line with "newString". Go to step 8.
7. Add new information "newString_2", without deleting the existing.
8. Save and close the text file.
Thanks.
I would recommend to put the getline command into the while loop because then it won't stop only because of EOF but when getline is not able to read anymore.
Like when the error bad occurs (which happens when someone deleted the file while your program was reading it).
It seems like you want to search inside a string, so "find" might be quite helpful.
#include <iostream>
#include <fstream>
#include <string>
int main (){
std::fstream yourfile;
std::string line, someString;
yourfile.open("file.txt", ios::in | ios::app); //The path to your file goes here
if (yourfile.is_open()){ //You don't have to ask if the file is open but it's more secure
while (getline(line)){
if(line.find(someString) != string::npos){ //the find() documentation might be helpful if you don't understand
if(someString == "A"){
//code for replacing the line
}
else{
yourfile << "newString_2" << endl;
}
} //end if
} //end while
} //end if
else cerr << "Your file couldn't be opened";
yourfile.close();
return 0;
}
I can't tell you how to replace a single line in a text file but I hope you can work with that little I can give you.
This should be a good start:
// basic file operations
#include <string>
#include <fstream>
int main ()
{
std::fstream myfile;
std::string line;
while (!myfile.eof())
{
std::getline(myfile,line); // Check getline() doc, you can retrieve a line before/after a given string etc.
//if (line == something)
//{
// do stuff with line, like checking for content etc.
//}
}
myfile.close();
return 0;
}
More informations here

C++ File not read?

I want to read a file line by line. Did something like
void Parse (string filepath) {
ifstream sourceFile;
sourceFile.open(filepath);
for (string line; getline(sourceFile, line);) {
cout << "1" << endl;
cout << line << endl;
}
}
int main() {
Parse("C:\\test.txt");
getchar();
return 0;
}
Then put some text into C:\test.txt, but when I run, I dont get anything. Why? Not even the "1". I notice no exception if the file is not there too. I suppose that a sign of a problem?
You have to check for success/error manually. Try with ifstream::good():
sourceFile.open(filepath);
if(!sourceFile.good()) {
// do something
If you don't want to check manually, you can enable exceptions:
// call that before open()
sourceFile.exceptions ( ifstream::failbit | ifstream::badbit );
I think you have problems opening the file. I would suggest two things:
check if sourceFile is opened successfully(if (sourceFile))
debug the code and see the code path your code follows.
EDIT: adding the actual solution to the problem in my answer(instead of just a comment) so that people won't miss it:
Here is one more thought - check the file name in its properties. Has happened to me that if windows hides the extension of the file the name is actually test.txt.txt, while what I see displayed is only test.txt.
change your for loop to
for (string line; sourceFile.good();) {
getline(sourceFile, line);
}
This way, you check the validity of your stream in the conditional part of the for, and get the line if the stream good.

getline() reads an extra line

ifstream file("file.txt");
if(file.fail())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
cout<<line[l++]<<"\n";
}
}
I am using a two dimensional character array to keep the text (more than one line) read from a file to count the number of lines and words in the file but the problem is that getline always reads an extra line.
Your code as I'm writing this:
ifstream file("file.txt");
if(file.fail())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
cout<<line[l++]<<"\n";
}
}
The first time getline fails, you still increment the line counter and output the (non-existing) line.
Always check for an error.
extra advice: use std::string from the <string> header, and use its getline function.
cheers & hth.
The problem is when you're at the end of the file the test on file will still succeed because you have not yet read past the end of file. So you need to test the return from getline() as well.
Since you need to test the return from getline() to see if it succeeded, you may as well put it right in the while loop:
while (file.getline(line[l], 80))
cout << line[l++] << "\n";
This way you don't need a separate test on file and getline().
This will solve your problem:
ifstream file("file.txt");
if(!file.good())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
if(!file.eof())
cout<<line[l++]<<"\n";
}
}
Its more robust
Does the file end with a newline? If it does, the EOF flag will not be triggered until one extra loop passes. For example, if the file is
abc\n
def\n
Then the loop will be run 3 times, the first time it will get abc, the second time it will get def and the third time it will get nothing. That's probably why you see an additional line.
Try checking the failbit on the stream AFTER the getline.
Only do the cout if file.good() is true. The extra line you're seeing comes from the last call to file.getline() which reads past the end of the file.

ifstream fails to open in recursive calls

we are running into an odd issue when trying to parse an input file. the idea is that this file can include other files, which must be parsed as well. We are doing this recursively in a function defined as
int parse_inp(const char* filename)
The main file parses no problem, but recursive calls cannot open their file streams.
int parse_inp(const char* filename)
{
char buffer[BUFFER_MAX+1];
char* token;
std::string tok;
int keywordSection;
bool end_of_file;
int cardNum;
...
int i;
std::string tempop;
double tempd1, tempd2;
SetSegmentCard2 tempSetSegmentCard2;
int offset;
printf("%s\n", filename);
std::ifstream inp;
inp.clear();
inp.open(filename, std::ios::in);
if(!inp.good() || !inp.is_open())
{
char path1[256];
getcwd(path1,256);
printf("CWD: %s\n", path1);
fflush(NULL);
printf("Unable to open '%s'\n", filename);
return 0;
}
std::set<std::string> unrecognized;
std::string line;
while(inp.good() && !inp.eof())
{
getline(inp, line);
strcpy(buffer, line.c_str());
if (isComments(buffer)) //skip the comments line
continue;
if (buffer[0]=='*') //this is a keyword line
{
token = strtok(buffer," \n");
keywordSection = is_inp_keyw(token);
if (keywordSection==0)
unrecognized.insert(token);
cardNum = 0;
continue;
}
//a data line
tempop="";
char* found = NULL;
char path_buffer[100] = "Dyna3DWriter\\";
int pos = 0;
switch(keywordSection)
{
case 0: //not recognized
//end of last keyword, not recognizable word
break;
case 1: //KEYWORD
//"KEYWORD didn't do anything
break;
case 2: //TITLE
break;
case 3: //INCLUDE
token = strtok(buffer, "\n");
inp.clear();
parse_inp(token);
break;
...
}
}
if(inp.is_open())
{
inp.close();
inp.clear();
}
}
The recursive files never parse. I looked around a lot and most issues seemed to be either that the fail bit was set (which is why we are calling inp.clear() a lot), or that we are making the wrong assumption about the current working directory.
To test the second theory, we added in:
if(!inp.good() || !inp.is_open())
{
char path1[256];
getcwd(path1,256);
printf("CWD: %s\n", path1);
fflush(NULL);
printf("Unable to open '%s'\n", filename);
return 0;
}
And our working directory and file name are both correct. We see the same behavior when using fopen(filename, "r") --- a call to perror("fopen") results in:
fopen: no such file or directory
EDIT: Filled in more code
Are you sure the filename does not contain any garbage or bad character that would lead to this issue?
If the error is file not found, that means the filename is wrong in some way.
Could it come from a bad declaration of buffer? We don't see it in your code.
Another possibility is that you use strtok again in your initialization before opening the file. You must avoid using strtok that is based on global storage for recursive method like this. You should use strtok_r instead.
If your recursive function is called very deeply you can easily overload the OS limit on the number of open files.
I used to run my Gentoo Linux with a per-process file limit of 250 with exceptions for programs that needed a lot more.
Windows has limits that vary depending on how much memory is available to the system and how many objects have already been created system-wide.
The smart way to do this is to have two functions. The first function is the one everyone else calls and it does the setup, including opening the file. The second function is the recursive function and it takes nothing but a reference to a std::ifstream object.
EDIT:
I see that I misunderstood your question, and you aren't recursively opening the same file. I will leave my above paragraph anyway.