Why does std::ifstream does not update if defined globally - c++

I'm trying to write a simple tail program in C++. I've tried the example from this solution and it works like a charm.
Then, I tried to make the ifstream as global. Now the code does not work anymore and nothing is showed if I edit the file.
Why this behaviour? I read the manual of ifstream::open and I don't see any kind of error but the code does not work:
Opens the file identified by argument filename, associating it with
the stream object, so that input/output operations are performed on
its content. Argument mode specifies the opening mode.
Here is the non-working code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int find_last_linefeed(ifstream &infile) {
infile.seekg(0,ios::end);
int filesize = infile.tellg();
for(int n=1;n<filesize;n++) {
infile.seekg(filesize-n-1,ios::beg);
char c;
infile.get(c);
if(c == 0x0A) return infile.tellg();
}
}
ifstream infile;
int main() {
int last_position=-1;
for(;;) {
infile.open("/Users/alberto/tmp/test");
int position = find_last_linefeed(infile);
if(position > last_position) {
infile.seekg(position,ios::beg);
string in;
infile >> in;
cout << in << endl;
}
last_position=position;
sleep(1);
}
}
Here is the working code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int find_last_linefeed(ifstream &infile) {
infile.seekg(0,ios::end);
int filesize = infile.tellg();
for(int n=1;n<filesize;n++) {
infile.seekg(filesize-n-1,ios::beg);
char c;
infile.get(c);
if(c == 0x0A) return infile.tellg();
}
}
int main() {
int last_position=-1;
for(;;) {
ifstream infile;
infile.open("/Users/alberto/tmp/test");
int position = find_last_linefeed(infile);
if(position > last_position) {
infile.seekg(position,ios::beg);
string in;
infile >> in;
cout << in << endl;
}
last_position=position;
sleep(1);
}
}

Since you open inside the loop, the stream will enter an error state on the second iteration, and keep failing after that.
Move
infile.open("/Users/alberto/tmp/test");
outside the loop, or define infile like this and don't use open at all:
ifstream infile("/Users/alberto/tmp/test");
The best alternative is to not use a global variable at all, as there's no reason for it here.
Also, find_last_linefeed fails to return anything if the file doesn't contain a linefeed, which is undefined.
—-
Regarding ifstream::open, from the standard (27.9.1.9):
Calls rdbuf()->open(s, mode | ios_base::in). If that function does not
return a null pointer calls clear(), otherwise calls setstate(failbit)
and basic_filebuf::open (27.9.1.4):
If is_open() != false, returns a null pointer

Related

c++ how to read and write from a specific line in a textfile?

hi I am trying to read a specific line from a text file update that and put it back to the same line without affecting the other lines in c++
here I am trying to execute the code and values get added when I re-execute it
#include <iostream>
#include <stream>
#include <stdio.h>
#include <string>
#include <stream>
using namespace std;
void stringGen(char num){
ifstream ifile;
ifile.open("example1.txt");
if(ifile) {
int LINE = 5;
string line;
ifstream myfile1 ("example1.txt");
for (int i = 1; i <= LINE; i++)
getline(myfile1, line);
cout << line<<endl;
stringstream geek(line);
int num=0;
geek>>num;
if(num<61004){
num=num+1;
ofstream MyFile("example1.txt");//
MyFile.close();
}
else{
num=61001;
ofstream MyFile("example1.txt");//
MyFile << num;
MyFile.close();
}
}
else{
int num=61001;
cout<<num<<endl;
ofstream MyFile("example1.txt");//
MyFile << num+1;
MyFile.close();
}
}
int main (){
char num;
stringGen(num);
return 0;
}
At first, you need to understand, how files, with lines are stored. Simplified, it is a sequence of bytes, one byte after the other. There maybe some special characters in this byte sequence, which people can interprete as the end of a line, e.g. '\n'. But also other characters or even more than one character is possible:
If you look at the following text.
Hello1
World1
Hello2
World2
it maybe stored in a file like this:
Hello1\nWorld1\nHello2\nWorld2\n
And just because we interprete a '\n' as the end of the line, we can "see" lines in there.
So, if you want to modify a line, then you would need to find the start position of the thing that we interprete as a line in the file, and then modify some bytes.
That can of course only be done, if the length of the "line" will not change. Then you could use "seek" functions and overwrite the needed bytes.
In reality, nobody would do that. Normally, you would read "lines" of the file into some kind of memory buffer, then do the modification there and then write back all lines.
For example, you would define a std::vector and then read all lines, by using std::getline and push_back the lines in the std::vector.
The modifications will be done in the std::vector, and the all data will be written back to the file, overwriting all "old" data.
There are more answers to this question. If you have any more specific question, I will answer again
Some simple example code
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
int main() {
// Here we will store all lines of the text file
std::vector<std::string> lines{};
// Open the text file for reading and check, if it could be opened
if (std::ifstream textfileStream{ "test.txt" }; textfileStream) {
// Read all lines into our vector
std::string oneLine{};
while (std::getline(textfileStream, oneLine)) {
// Add the just read line to our vector
lines.push_back(oneLine);
}
// For test purposes, modify the first line
if (not lines.empty()) lines[0] = "MODIFIED";
}
else std::cerr << "\nError: Could not open input text file\n";
// Write back data
// Open the text file for writing and check, if it could be opened
if (std::ofstream textfileStream{ "r:\\test.txt" }; textfileStream) {
// Iterate over all lines and wriite to file
for (const std::string& oneLine : lines)
textfileStream << oneLine << '\n';
}
else std::cerr << "\nError: Could not open output text file\n";
return 0;
}
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <string>
#include <sstream>
using namespace std;
void stringGen(char num){
int count=0;
int a;
string line,check,linex;
string msg="Message_Handler:";
fstream ifile;
ifile.open("sample.txt",ios::in|ios::out);
if(ifile){
while(getline (ifile,line)) {
if (line.find("Message_Handler:") == 0){
check=line.substr(16,5);
count++;
a=ifile.tellp();
}
}
ifile.close();
if(count==0){
int num=61001;
cout<<num<<endl;
num=num+1;
ofstream examplefile ("sample.txt",ios::app);
examplefile<<"Message_Handler:"<<num;
examplefile.close();
}
if(count==1){
cout<<check<<endl;
stringstream geek(check);
int num=0;
geek>>num;
if(num<61004){
num=num+1;
stringstream ss;
ss << num;
string nums = ss.str();
fstream MyFile("sample.txt",ios::in|ios::out);
MyFile.seekp(a-5);
MyFile<<nums;
}
else{
int num=61001;
stringstream ss;
ss << num;
string nums = ss.str();
fstream MyFile("sample.txt",ios::in|ios::out);
MyFile.seekp(a-5);
MyFile<<nums;
}
}
}
else{
int num=61001;
cout<<num<<endl;
ofstream MyFile("sample.txt",ios::app);
MyFile <<"Message_Handler:"<< num+1;
MyFile.close();
}
}
int main (){
char num;
stringGen(num);
return 0;
}

I'm stuck on how to read from a text file

/* In the text file I have a char followed by a blankspace then a string. I'm trying to read the char and string into seperated arrays. Any help is appreciated */
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
int main()
{
char arrivOrDepart;
string licensePlt;
ifstream inFile;
inFile.open("Text.txt");
if (!inFile)
{
cout << "Can't open file" << endl;
return 1;
}
for (int i = 0; i < 4; i++)
{
getline(cin, arrivOrDepart[i]);
getline(cin, licensePlt[i]);
}
inFile.close();
cin.get();
return 0;
}
//text file
A QWE123
A ASD123
A ZXC123
A WER123
A SDF123
#include <fstream>
#include <iterator>
#include <vector>
this reads from file into vector
std::ifstream input("d:\\testinput.txt");
std::vector<std::string> bytes(
(std::istreambuf_iterator<std::string>(input)),
(std::istreambuf_iterator<std::string>()));
input.close();
then, just put the data into whatever container you want. you should almost always prefer vector over array btw
There are a few problems with the code:
getline is the wrong tool of choice for this. if you want to split a stream based on spaces, use >>.
arrivOrDepart and licensePlt are not defined as arrays but are used as arrays.
reading from cin, not from file.
My suggested fixes (excluding using vectors instead of arrays):
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std; // avoid using this
int main()
{
const int MAXARRAY = 4; // avoid using magic numbers
char arrivOrDepart[MAXARRAY]; // made an array, but prefer std::vector
string licensePlt[MAXARRAY]; //made an array
ifstream inFile;
inFile.open("Text.txt");
if (!inFile)
{
cout << "Can't open file" << endl;
return 1;
}
string temp;
int i = 0;
while (i < MAXARRAY && // not overrunning the arrays
inFile >> temp >> licensePlt[i] && // read data from file stream
temp.length() == 1) // read only one character for arrivOrDepart
{
arrivOrDepart = temp[0];
i++;
}
inFile.close();
cin.get();
return 0;
}
Recommended reading:
Why is "using namespace std" considered bad practice?
What is a magic number, and why is it bad?
std::vector documentation (Alternate easier to read but often less accurate documentation)
std::getline documentation. Note the third parameter used to set the parsing delimiter.

Deleting the contents of the file not working

I'm writing, reading and deleting the content of a file. Eeverything works fine except the delete part, as when I press y it says deleted but doesn't display any records.
typedef struct ch
{
char str[10];
};
void disp(ch d)
{
cout<<"\n"<<d.str<<"\n";
}
//delete part
cout<<"\nwant to delete??";
char c;
cin>>c;
if(c=='y')
{
char s[10];
cout<<"nter - ";
cin>>s;
file.seekg(0);
int found=0;
fstream temp("temp.dat",ios::in|ios::out|ios::app);
while(file.read((char *)&dta,sizeof(dta)))
{
if(strcmp(dta.str,s)==0)
{
found=1;
cout<<"deleted";
}
else
temp.write((char *)&dta,sizeof(dta));
}
if(!found)
cout<<"not found";
remove("new.dat");
rename("temp.dat","new.dat");
temp.close();
file.open("new.dat",ios::in|ios::out|ios::app);
}
EDIT: Looking over your code again, I see the problem is that you are using the ios::app although you have also passed ios::in.
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.
http://www.cplusplus.com/doc/tutorial/files/
Old Post:
Take a look at the following code example:
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
using namespace std;
const char g_szTestData[] = "This is some test da$$$$######ta and some more"
" tes$$$$######ting";
int main(int argc, char** argv)
{
fstream file("new.dat", ios::in|ios::out);
file << g_szTestData << flush;
cout << "Do you want to delete all $$$$######?";
if (cin.get() == 'y')
{
char szBuffer[10]; //< File read buffer
char szString[11] = "$$$$######"; //< 10 characters + '\0'
bool bFound = false;
fstream temp("temp.dat", ios::out);
file.seekg(0, file.beg);
while (file.read(szBuffer, sizeof(szBuffer)))
{
if (strncmp(szBuffer,szString,10) == 0) bFound = true;
else temp.write(szBuffer, sizeof(szBuffer));
}
temp.flush();
temp.close();
if (bFound)
{
file.close();
remove("new.dat");
rename("temp.dat", "new.dat");
file.open("new.dat",ios::in|ios::out);
}
else cout << "Pattern Not Found!" << endl;
}
/* Do something with the contents of file. */
// Lets clean up at the end
file.close();
return 0;
}
In this example, a file is created and the contents g_szTestData are added, you can verify this by opening the file (before pressing 'y').
The user is then asked if they would like to delete a string of 10 characters $$$$###### from the file. If the user wishes to proceed, a new file is opened temp.dat. The program gradually walks through the existing new.dat file (10 characters at a time). If the string the program reads from new.dat is not the target string, the string is written to the temp file.
If the target string is found, both files are closed, the old file is deleted and the new file is renamed to the name of the old file. The new file is then opened so the program can do additional work on its contents.
Instead of having a fixed 10 character string, it is possible to ask the user for the string they wish to remove using cin >> szString but the string would need to be 10 characters long.
Your code work as expected, it only display deleted because disp() isn't called anywhere.
#include <iostream>
#include <string.h>
#include <fstream>
#include <stdio.h>
using namespace std;
typedef struct {
char str[16];
} ch;
static void disp(ch d) {
cout<<d.str<<"\n";
}
int main(int argc, char **argv) {
fstream file;
ch dta;
file.open("new.dat",ios::in|ios::out|ios::trunc);
for (int i=0; i<3; i++) {
snprintf((char *)&dta.str, sizeof(dta.str)-1, "rec%d", i);
file.write((char *)&dta,sizeof(dta));
}
//delete part
{
char s[16] = "rec1";
file.seekg(0);
int found=0;
fstream temp("temp.dat",ios::out);
while(file.read((char *)&dta,sizeof(dta))) {
if(strcmp(dta.str, s)==0) {
found=1;
cout<<"deleted * ";
disp(dta);
} else {
temp.write((char *)&dta,sizeof(dta));
disp(dta);
}
}
if(!found)
cout<<"not found";
file.close();
remove("new.dat");
rename("temp.dat","new.dat");
temp.close();
}
}

how to count the characters in a text file

im trying to count the characters inside a text file in c++, this is what i have so far, for some reason im getting 4. even thou i have 123456 characters in it. if i increase or decrease the characters i still get 4, please help and thanks in advance
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const char FileName[] = "text.txt";
int main ()
{
string line;
ifstream inMyStream (FileName);
int c;
if (inMyStream.is_open())
{
while( getline (inMyStream, line)){
cout<<line<<endl;
c++;
}
}
inMyStream.close();
system("pause");
return 0;
}
You're counting the lines.
You should count the characters. change it to:
while( getline ( inMyStream, line ) )
{
cout << line << endl;
c += line.length();
}
There are probably hundreds of ways to do that.
I believe the most efficient is:
inMyStream.seekg(0,std::ios_base::end);
std::ios_base::streampos end_pos = inMyStream.tellg();
return end_pos;
First of all, you have to init a local var, this means:
int c = 0;
instead of
int c;
I think the old and easy to understand way is to use the get() function till the end char EOF
char current_char;
if (inMyStream.is_open())
{
while(inMyStream.get(current_char)){
if(current_char == EOF)
{
break;
}
c++;
}
}
Then c will be the count of the characters
this is how i would approach the problem:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main ()
{
string line;
int sum=0;
ifstream inData ;
inData.open("countletters.txt");
while(!inData.eof())
{
getline(inData,line);
int numofChars= line.length();
for (unsigned int n = 0; n<line.length();n++)
{
if (line.at(n) == ' ')
{
numofChars--;
}
}
sum=numofChars+sum;
}
cout << "Number of characters: "<< sum << endl;
return 0 ;
}
Just use good old C FILE pointers:
int fileLen(std::string fileName)
{
FILE *f = fopen(fileName.c_str(), "rb");
if (f == NULL || ferror(f))
{
if (f)
fclose(f);
return -1;
}
fseek(f, 0, SEEK_END);
int len = fell(f);
fclose(f);
return len;
}
I found out this simple method , hope this helps
while(1)
{
if(txtFile.peek() == -1)
break;
c = txtFile.get();
if(c != txtFile.eof())
noOfChars++;
}
This works for sure, it is designed to read character by character.
It could be easily put into a class and you may apply function for every char, so you may check for '\n', ' ' and so on. Just have some members in your class, where they can be saved, so you may only return 0 and use methods to get what exactly you want.
#include <iostream>
#include <fstream>
#include <string>
unsigned long int count(std::string string)
{
char c;
unsigned long int cc = 0;
std::ifstream FILE;
FILE.open(string);
if (!FILE.fail())
{
while (1)
{
FILE.get(c);
if (FILE.eof()) break;
cc++; //or apply a function to work with this char..eg: analyze(c);
}
FILE.close();
}
else
{
std::cout << "Counter: Failed to open file: " << string << std::endl;
}
return cc;
};
int main()
{
std::cout << count("C:/test/ovecky.txt") << std::endl;
for (;;);
return 0;
}
C++ provides you with a simple set of functions you can use to retrieve the size of stream segment.
In your case, we want to find the file end, which can be done by using fstream::seekg, and providing the fstream::end.
note that fstream is not implementing the end iterator overload, this is it's own end constant
When we've seeked towards the end of the file, we want to get the position of the stream pointer, using tellg (also known as the character count in our case).
But we're not done yet. We need to also set the stream pointer to its original position, otherwise we'll be reading from the end of the file. Something we don't want to do.
So lets call fstream::seekg again, but this time set the position to the begining of the file using fstream::beg
std::ifstream stream(filepath);
//Seek to end of opened file
stream.seekg(0, stream.end);
int size = stream.tellg();
//reset file pointer to the beginning of the file
stream.seekg(0, stream.beg);

Help with the ifstream function in C++

I've read a lot of tutorials and can not really find anything comprehensive on this subject.
I had written the following code to do one function:
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
ifstream ReadFile( ifstream& openInputFile, string& sLine, int& chgLine );
int main()
{
int chgLine;
string MyFile, sLine,
sFile = "test.txt";
cout << "Enter a file: ";
cin >> MyFile ;
ifstream openInputFile;
if ( MyFile != sFile ) // file must be 'hello.cpp' to proceed
{
cout << "Error";
exit(0);
}
// if correct file is entered print the contents of it
ReadFile( openInputFile, sLine, chgLine );
system("pause");
return 0;
}
ifstream ReadFile( ifstream& openInputFile, string& sLine, int& chgLine )
{
while ( getline ( openInputFile, sLine ) )
{
if ( sLine.length() == 0 ) continue; // to proceed
for ( chgLine = 0; chgLine < sLine.length(); chgLine++ )
{
if ( sLine[chgLine] >= 97 && sLine[chgLine] <= 122 || sLine[chgLine] >= 65 && sLine[chgLine] <= 90 )
{
cout << sLine[chgLine];
}
}
}
}
But now I've decided to break all of this up into three functions that do what I want separately, and then call them from the main() function.
The first function opens the file:
#include <iostream>
#include <fstream>
using namespace std;
ifstream openInputFile()
{
// use a pointer..return a pointer
// ifstream definition is the challenge
ifstream *fp;
//fp = new ifstream openInputFile;
return openInputFile;
}
int main()
{
cout << "Hello, world!" << endl;
system("pause");
return 0;
}
I get stuck trying to return a pointer. I don't understand what I'm doing wrong. How can I get the last bit of code to work? And how does one return a pointer with ifstream if it has the function's type?
The C++ way to do this would be to create a class that wraps up all of the opening, reading and writing. Note that this would also handle closing the file automatically, a good example of RAII.
// FancyFile.h:
class FancyFile
{
private:
std::ifstream stream;
public:
void DoMagic();
InputFile(const std::string FilePath)
{ stream.open(FilePath.c_str()); }
~InputFile(void)
{ stream.close(); }
};
// FancyFile.cpp:
#include <fstream>
#include <string>
#include "FancyFile.h"
void FancyFile::DoMagic()
{
//.. your custom file handling code goes here
}
// main:
#include "FancyFile.h"
int main()
{
FancyFile myFancyFile("test.txt");
myFancyFile.DoMagic();
system("pause");
return 0;
}
It seems you shouldn't need to return by pointer in the above case.
If you do need to return a modified result, you may want to look into return value optimization(RVO). With RVO, you save yourself copying of temporaries (and in your case dynamic allocation) by passing in the output parameter by reference.
For example
void openInputFile(ifstream& file)
{
file.open(...);
}
int main()
{
ifstream file;
openInputFile(file);
// Work with file, since file is
// on the stack you don't have to worry about cleanup/etc
}
This gets rid of a lot of the problems dealing with dynamic allocation and may avoid the problems you are coming across. You seem to be kind-of onto this, so I'm puzzled as to the need to return the ifstream.
If you need do need to return by pointer, in that case in seems all your missing is an asterisk. For example this function:
ifstream* Blah();
should work.
The example doesn't actually show you opening the ifstream with the file name specified?
The example is requesting user input of the file name, and then saying it can only be one value?
As Doug T. said, it would probably be nicer from the example code to make use of scope to manage the ifstream.
Anyway, here is an example returning a pointer
ifstream * openInputFile( ) {
string MyFil;
string sFile = "test.txt";
cout << "Enter a file: ";
cin >> MyFile ;
if ( MyFile == sFile )
{
ifstream * inputFile = new ifstream( MyFile.c_str() );
// do some other stuff here?
return inputFile;
} else {
return null;
}
}
Then main can be
int main()
{
int chgLine;
string sLine;
ifstream *inputFile = openInputFile();
if( NULL == inputFile )
{
cout << "Error";
exit(0);
}
...