How to write to .txt file without recreating existing file - c++

I have basic logging process. When an error occured in the program, it has to been logging to a .txt file. I use following code for this:
#include <fstream>
fileName = "logs/error_log.txt";
ofstream myfile;
myfile.open (fileName,fstream::app);
myfile << serialized_string << endl;
myfile.close();
When an error occured it goes to error_log.txt file successfully. But when program crashed and is restarted afterward, new logs are not logged as append. As expected the way I am using creates a new file which has same name existed file and write on it. Can someone explain me how should I write older logs as well?
Edit: These are steps I have faced:
I am using raspbian and I compile with following:
g++ main.cpp -lwiringPi -lpthread -lcurl -o test
That is the whole function.
int putLog(const char* process, int logType, string logData) {
isLoggerBusy = true;
string fileName;
std::string color;
switch (logType) {
case 0:
fileName = "logs/error_log.txt";
// color = "\033[0;31m";
break;
case 1:
fileName = "logs/info_log.txt";
// color = "\033[0;36m";
break;
case 2:
fileName = "logs/state_log.txt";
// color = "\033[1;33m";
break;
}
if (process == "WebSocket") {
color = "\033[1;32m";
}
json j = {
{"Process", process}, {"Time", currentDateTime()}, {"Log", logData}};
string serialized_string = j.dump();
fix_utf8_string(serialized_string);
ofstream myfile;
myfile.open(fileName, fstream::app);
cout << color << serialized_string << '\n';
myfile << serialized_string << endl;
myfile.close();
isLoggerBusy = false;
cout << "\033[0m" << endl;
return 0;
}
I started the program. It write downs these lines to the state_logs.txt
{"Log":"Incoming
Message{\"Action\":\"Heartbeat\",\"Data\":null}","Process":"WebSocket","Time":"2018-08-16.14:53:52"}
{"Log":"GSM Setup
Finished","Process":"SMSService","Time":"2018-08-16.14:54:13"}
Stopped the program with CTRL-C and control the state_logs.txt and I can see now two lines there.
Restart the program and interrupt with CTRC-C again in 10 seconds (before a new line logging.)
I check the state_logs.txt again and now I can not see nothing. Re-did this process but this time waiting a bit more before interrupt program(just a bit more to get only one line of log.). So now I can see only one and timestamps has been changed.

I cannot reproduce what OP describes.
I just tested on cygwin/Windows 10. (I didn't know how to make this test on an online compiler.)
testFStreamApp.cc:
#include <iostream>
#include <fstream>
int main()
{
std::cout << "Log error...\n";
{ std::ofstream log("testFStream.log", std::ios::out | std::ios::app);
log << "Error happened!" << std::endl;
}
std::cout << "Going to die...\n";
abort();
return 0; // should never be reached
}
Test Session:
$ g++ -std=c++11 -o testFStreamApp testFStreamApp.cc
$ rm testFStream.log
$ for i in 1 2 3; do
> echo "$i. start:"
> ./testFStreamApp
> done
1. start:
Log error...
Going to die...
Aborted (core dumped)
2. start:
Log error...
Going to die...
Aborted (core dumped)
3. start:
Log error...
Going to die...
Aborted (core dumped)
$ cat <testFStream.log
Error happened!
Error happened!
Error happened!
$
YSC pointed out that I made some silent changes. I did it assuming no relevance.
However, to erase any excuses, I tried also:
#include <iostream>
#include <fstream>
int main()
{
std::cout << "Log error...\n";
std::ofstream log;
log.open("testFStream.log", std::fstream::app);
log << "Error happened!" << std::endl;
log.close();
std::cout << "Going to die...\n";
abort();
return 0; // should never be reached
}
The output was exactly as above.
I hadn't dared to test this but doctorlove encouraged me:
#include <iostream>
#include <fstream>
int main()
{
std::cout << "Log error...\n";
std::ofstream log;
log.open("testFStream.log", std::fstream::app);
log << "Error happened!" << std::endl;
std::cout << "Going to die...\n";
abort();
log.close();
return 0; // should never be reached
}
Even in this case, I got the same result.
At this point, I must admit that cygwin is just a wrapper around the win32 API. So, in this case, I wouldn't wonder if this behaves different on other OSes.
I'm aware that std::endl does a flush() insight. The question is how far down (into the system) the flush() is effective. (In daily work, I try to write the code in a way that it is not necessary to rely on such details...) ;-)

Related

writing a c++ linux program that should run as root

i am trying to write a linux program that uses the c++ mount function (code below),
however, the mount operation requires permmissions, and running the program throws the errno 'Operation not permitted' (printed using perror)
tried some SO solutions but non was helpful, the alternative is to use the system("sudo mount..") but i prefer the c++ function.
is ther a way to use this function with permmissions?
IDE: Clion 2020.2.4
relevant code below
int returnValue = mount(sourcePath,targetPath,"", MS_SHARED, ""); //mounting the device
if (returnValue==0){
//mount completed
//somecode
}else{
//mount failed
std::cout<<"mount failed\n";
perror("");
}
output
mount failed
Operation not permitted
After you compile the code, change the ownership of the file to the superuser with chown root filename and add "set user or group ID on execution" to the mode of the executable file with chmod u+s filename.
Some options I see:
Just run the binary as root or under sudo;
Use setcap cap_sys_admin+ep on your binary to grant it the CAP_SYS_ADMIN capability;
If the set of possible targetPaths is fixed, edit /etc/fstab to give these paths the userflag.
#include <fstream>
#include <iostream>
#include <string>
int main(int argc, char *argv[]){
std::ifstream tmpfile;
std::string tmpfile_name = ".mytempfile.tmp";
std::string command = "groups>";
std::string searchv[] = {"disk", "sudo", "root"};
int searchc = sizeof(searchv)/sizeof(searchv[0]);
int search_matches = 0;
char data_buffer[128];
if(!system(NULL)) goto ERROR;
command += tmpfile_name;
if(system(command.c_str()) != 0) goto ERROR;
std::cout << "executed external command: \"" << command << "\"" << std::endl;
tmpfile.open(tmpfile_name, std::ios::in);
if(!tmpfile.is_open()) goto ERROR;
std::cout << tmpfile_name << " opened" << std::endl;
do{
tmpfile >> data_buffer;
if(tmpfile.eof()) break;
if(tmpfile.fail()) goto ERROR;
for(int i = 0; i < searchc; i++){
if(!searchv[i].compare(data_buffer)){
search_matches++;
std::cout << "found group " << searchv[i] << std::endl;
}
}
}
while(tmpfile.good());
tmpfile.close();
std::cout << "found " << search_matches << " groups" << std::endl;
return EXIT_SUCCESS;
ERROR:
std::cerr << "something bad happened" << std::endl;
return EXIT_FAILURE;
}
This answer may be off-topic, sorry for that.
This program calls the external Linux program "groups" and searches for keywords "disk", "sudo", "root", which indicating user access rights for mounting a disk.
accessing an os function implies complying with that os's security model.
so short answer, no. you can't override security models in your user-run code

unexpected getline behavior

Originally was trying to read data using char* but switched to string cause was getting behavior as if there was a missing null terminator. made the problem minimal below but still getting very weird output
int main(int argc, char * argv[]){
// file one: flightData
std::ifstream inFile1(argv[1]);
if (!inFile1.is_open())
{
std::cout << "Could not open the file 1." << std::endl;
return -1;
}
// file two: flightPlans
std::ifstream inFile2(argv[2]);
if (!inFile2.is_open())
{
std::cout << "Could not open the file 2." << std::endl;
return -1;
}
//File three: output
std::ofstream outputfile(argv[3]);
if (!outputfile.is_open())
{
std::cout << "Could not open the output file" << std::endl;
return -1;
}
std::string buffer;
getline(inFile1, buffer);
std::cout<<buffer<<std::endl;
while (getline(inFile1, buffer)) {
std::cout<<buffer;
std::cout<<"help";
}
// flightPlanner system(inFile1);
// system.printF();
// system.planFlights(inFile2,outputfile);
return 0;
}
output is
4
helpDallas|Austin|50|50help
which i'm pretty sure is incorrect, interestingly when i add endl to cout buffer it gives me output i would expect not really sure whats going on
inFile1
4
Dallas|Austin|50|50
Dallas|Austin|50|50
Dallas|Austin|50|50
Dallas|Austin|50|50
When i run in debugger i get the output i expect:
4
Dallas|Houston|50|50
helpDallas|Houston|50|50
helpDallas|Houston|50|50
helpDallas|Houston|50|50help
any idea what could be going on?
Do you need flushing your stdout?
std::cout << std::flush;
Any chance your shell ate your outputs?
Try pipping the output to "cat -A":
./a.out | cat -A
(Drive by comment - I may not know what I'm talking about ^_^)

fstream fails to write/open files on raspberry pi

I am trying to run a cpp program on raspberry pi 3 b+ (from 'pi' user) but when I try to open a file with 'fstream' library it doesn't work.
I am using the following code (from main):
std::ios::sync_with_stdio(false);
std::string path = "/NbData";
std::ofstream nbData(path);
if (!nbData) {
std::cout << "Error during process...";
return 0;
}
nbData.seekp(std::ios::beg);
The program always fails there and stops because no file is created (I don't get a fatal error but the test fails and it outputs 'Error during process' which means no file was created).
I am compiling with the following command (there are no issues when I compile):
g++ -std=c++0x nbFinder.cpp -o nbFinder
I have already tried my program on Xcode and everything worked perfectly...
The problem is your path. You must put the file, you are using just the path and if the path do not exist will throw an error. In your case you just using std::string path = "/NbData";, that is you path not your file.
To be able to open your file you need make sure your path exist. Try use the code bellow, he will check if the path exist case not will create and then try to open your file.
#include <iostream>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
std::ios::sync_with_stdio(false);
std::string path = "./test_dir/";
std::string file = "test.txt";
// Will check if thie file exist, if not will creat
struct stat info;
if (stat(path.c_str(), &info) != 0) {
std::cout << "cannot access " << path << std::endl;
system(("mkdir " + path).c_str());
} else if(info.st_mode & S_IFDIR) {
std::cout << "is a directory" << path << std::endl;
} else {
std::cout << "is no directory" << path << std::endl;
system(("mkdir " + path).c_str());
}
std::ofstream nbData(path + file);
if (!nbData) {
std::cout << "Error during process...";
return 0;
}
nbData.seekp(std::ios::beg);
return 0;
}

fstream test program crashes for some reason

I have been playing around with the fstream class in C++ to see if I am able to write some data to a text file(.txt). According to what I know, If the program tries to write to a file that does not exist then it would automatically create that file, am I wrong? This program is very simple and does not give me any compiler errors which means it builds fine. However for some reason it crashes when I run it.
Here is my code:
#include <iostream>
#include <string>
#include <stdlib.h>
#include <fstream>
std::fstream* myFile;
int main()
{
int age = 15;
std::string myName = "Javier Martinez";
std::string friendsName = "David Lyn";
//Backslash is a special character, use double backslash or forward slash instead.
myFile->open("C:/Users/NIKE/Desktop/data.txt");
if (myFile->fail())
{
std::cerr << "File was unable to be opened.";
}
*myFile << age << "\n";
*myFile << myName << "\n";
*myFile << friendsName << "\n";
myFile->close();
std::cout << "File was successfully written to with the data";
return 0;
}
Any help is appreciated. Thank you in advance.
NOTE: I am using the GNU GCC compiler with Code::Blocks IDE
myFile is uninitialized. Check it.( Allocate memory) or simply use fstream.
Your problem stems from the line:
std::fstream* myFile;
You only declared a pointer to a stream object, which is initialized to nullptr by reason of it being in the global scope. The fact that you tried accessing a non-existent object (invalid) through it, you invoked what is known as Undefined Behavior.
You do not need to allocate stream objects on the heap, rather, do:
std::fstream myFile;
On a side Note: Check your program control flow:
if (!myFile)
{
std::cerr << "File was unable to be opened.";
}
else{
myFile << age << "\n";
myFile << myName << "\n";
myFile << friendsName << "\n";
}

Double Clicked Compiled C++ Unix Executable Doesn't Open Existing File to Read Information From

I've been searching online to solve the above issue with no
success so far. I will describe the issue in more details below.
My program contains only one .cpp file. The program should display text from "test.txt" if this file is opened. Otherwise, it should display the "Failed to open ..." message. The issue follows:
I open terminal, go to the directory containing my file, compile and run with the usual commands: "g++ main.cpp" and "./a.out". When I run my program in this way, using terminal directly, the program works correctly. It displays text when the text file exists and outputs error when it doesn't exist. When I double click the unix executable "a.out", even though the text file exists and is put side by side with the executable, the program displays "Failed to open ..." message. I don't know what to think at that point. Should code contain anything else besides what is below?
Operating system: OS X 10.9.5
#include <iostream>
#include <fstream>
using namespace std;
const int MAX_CHAR_READ = 100;
int main(int argc, const char * argv[])
{
ifstream read_file;
cout << endl << endl;
//Allocate dynamic memory
char * file = new char[strlen("test.txt") + 1];
char * text_line = new char[MAX_CHAR_READ + 1];
strcpy(file, "test.txt");
//Attempt to open a file for reading
read_file.open(file);
if(read_file.is_open() == true)
{
cout << "File: " << file << " is open!" << endl;
read_file.get(text_line, MAX_CHAR_READ, ';');
cout << text_line << endl;
read_file.close();
}
else
cout << "Failed to open: " << file << endl;
cout << endl << endl;
//Deallocate dynamic memory
delete [] file;
delete [] text_line;
return 0;
}
Program execution example using terminal manually:
$ cd Desktop/Other/Test
$ g++ main.cpp
$ ./a.out
File: test.txt is open!
Hello World!
$
Program execution example double clicking the same executable:
$/Users/vladimirmeshcheryakov/Desktop/Other/Test/a.out ; exit;
Failed to open: test.txt
logout
[Process completed]
one of the possible things to cause it could be the case of running the terminal as superuser, in a folder with access restriction to the regular user. (superuser doesn't have that restriction)
solution: give current user the right to Read/Write in this folder.
Now I need to find a solution of obtaining the path to executable.
Check whether argv[0] contains it.