I use an fstream object to write data to a text file. I write some initial data to it once and then write more data to it in a loop. Every time before writing to the file stream, I check whether it's open. Is this unnecessary? Should I only check once immediately after creating the fstream object?
Basically, I do this:
#define STOP 32700 // some value that indicates no more data is available
#include <string>
#include <exception>
#include <fstream>
int main (int argc, char* argv[])
{
try {
double data[5] {};
const std::string output_file_name {"name.txt"}
std::fstream outputFile (output_file_name, std::ios::out | std::ios::trunc);
if (outputFile.is_open()) // successfully opened file
outputFile << "initial text\n";
else // if text file could not be opened
throw Fstream_Exception;
/* do some other stuff (in various threads) */
do { // ok, now get data and write it to the file!
getData(&data[0]);
if (outputFile.is_open())
outputFile << data[0] << '\n';
else
throw Fstream_Exception;
} while (data[0] != STOP);
}
catch (Fstream_Exception& fstream_exception) {
/* handle exception */
}
}
The stream itself can throw exceptions when an error occurs. Just use its exceptions() method and pass the types of errors you want it to detect. This is more convenient than checking the state flags after each operation.
Related
I am using std::fstream to read and write to the same file. I can see the write happening but not the read.
After searching the web, I got to know that I can not set in and app mode together. So, got rid of that and made it very simple of not passing any arguments.
I am very interested to know the reason why read is not happening.
Also, how do people read and write to the same file using same fstream?
My code:
#include<iostream>
#include<fstream>
int main() {
std::fstream* fs = new std::fstream("xx.txt");
*fs << "Hello" << std::endl;
(*fs).close(); // ?
std::string line;
while(std::getline(*fs, line)) {
std::cout << line << std::endl;
}
}
With this code, I can xx.txt contain "Hello" as its content but it does not go inside the while loop at all stating that reading failed.
How can I overcome this?
You forgot to reopen the stream. Actually you can't open a stream in both directions (at the same time).
So the steps are:
Open the stream for writing
Write data
Close the stream
Reopen the stream for reading
Read data
Close it (optional)
Your sample can be rewritten as:
#include <fstream>
#include <iostream>
int main()
{
const std::string file_path("xx.txt");
std::fstream fs(file_path, std::fstream::app);
if(fs) // Check if the opening has not failed
{
fs << "Hello" << std::endl;
fs.close();
}
fs.open(file_path, std::fstream::in);
if(fs) // Check if the opening has not failed
{
std::string line;
while(std::getline(fs, line))
{
std::cout << line << std::endl;
}
fs.close();
}
return 0;
}
Note that it is a good idea to check if the stream is successfully open before trying to use it.
I will try to explain the issue.
Statement std::fstream* fs = new std::fstream("xx.txt"); will open file if it exists in default mode "in|out" .
If the file does not exist then the call to open from inside of constructor std::fstream will fail. And this can be checked by checking failbit using function fail(). So you will explicitly need to call 'open' to use the fstream object for data input. Note: the new file will not be created unless you call 'close'.
You can test this by actually trying to open an existing file or new file you can see the difference.
So alternatively what you should do is always call 'open' which will work in both cases (if file exists or not).
#include<iostream>
#include<fstream>
int main() {
//std::fstream fs("xx.txt");
//std::cout << fs.fail() << std::endl; // print if file open failed or passed
std::fstream fs;
fs.open("xx.txt", std::fstream::in | std::fstream::out | std::fstream::app);
std::cout << fs.fail() << std::endl;
fs << "Hello" << std::endl;
if (fs.is_open())
{
std::cout << "Operation successfully performed\n";
fs.close();
}
else
{
std::cout << "Error opening file";
}
For reading the content of the file you will first need to close the file. And then reopen and read. As I understand once you start using the object fs for insertion you cannot read from it unless you explicitly close it and reopen.
fs.open("xx.txt", std::fstream::in | std::fstream::out);
std::string line;
while(std::getline(fs, line)) {
std::cout << line << std::endl;
}
std::cout << "end" << std::endl;
fs.close();
}
I'm begginer in C++, so may be parts of my code doesn't have sense, sorry.
What I have to do is (C++, Linux, by fstream):
· Receive 3 or more files passed by terminal by:
./executable file1.txt file2.txt file3.txt
· programm a function that read the files file2.txt and file3.txt and copy it to file1.txt (concatenate, don't overwrite)
I don't know how to do it, I don't know anything about fstream, I'm just learning now by myself, so I really need help. Maybe there are similar questions solved in SO, but I don't know how to solve my problem by them.
I attach the code I have. I don't know how to code the function, so it's empty.
Thank you so much.
I try doing:
void concat(char *argv[], int numberoffilesreceived){
char c;
towritethefiles.open(argv[0], ios::app);
for(int i=1; i<numberoffilesreceived; i++){
toreadthefiles.open(argv[i], ios::in);
while(!toreadthefiles.eof()){
toreadthefiles >> c;
towritethefiles<< c;
}
}
}
It compiles but doesn't work, the program freezes when you run it.
and I also try using std::copy by I don't understand how it works.
ifstream toreadthefiles;
ofstream towritethefiles;
void concat(char *argv[], int numberoffilesreceived);
int main(int argc, char *argv[]){
/* 1/2 The code from below to 2/2 it's only to prevent path errors when receiving the files (it works fine) */
const char directory[SIZE]="./";
int count_files=0;
char files[SIZE][SIZE];
for(int i=1; i<argc; i++){
strcpy(files[i], directory);
strcat(files[i], argv[i]);
count_files++;
}
/*2/2 to add ./ to the name files when passed by terminal: ./executable ./file1.txt ./file2.txt ./file3.txt */
/*check if received almost 3 files like required */
if(argc<3){
cout<< "Error, to few files entered" << endl;
getchar();
exit(1);
}
/*pass the files to the concat function*/
for(int i=1; i<argc; i++){
concat(&argv[i], count_files);
}
toreadthefiles.close();
towritethefiles.close();
return 0;
}
void concat(char *argv[], int count_files){
}
I think I see an issue with your concat() function. You are calling concat() for each of the files passed in. Then in the function, you are using count_files to run that loop again for the number of files passed in.
I would consider rewriting concat() function so that it looks like this:
void concat(std::ofstream& outputStream, char* fileToAppend)
{
std::ifstream in(fileToAppend);
if (!in) {
cout << "Error, cannot open file: " << fileToAppend;
return;
}
// Use std::getline to read each line in the input stream,
// then write it to the output stream!
string line;
while (std::getline(in, line)) {
outputStream << line;
}
}
The benefit being that you can reuse the function for appending a single input file to an existing output stream, and you wrap up the check to ensure the file exists (you may want something more sophisticated like returning true/false on the file being appended, or throwing an error, etc.).
In main(), you would replace the code after the check for at least three files with something like:
// Create an output stream with the first file
// ios::out- output flag
// ios::app- append flag
std::ofstream out(argv[1], ios::out | ios::app);
// Make sure the file exists!
if (!out) {
cout << "Error, cannot open file: " << argv[1];
exit(1);
}
// For all other arguments, concat with the first.
for (int i = 2; i < argc; i++) {
concat(out, argv[i]);
}
You can use std::copy with stream iterators, and I've revised my previous suspicion that it would be slow, so here's one way using doing just that with comments in the code.
#include <iostream>
#include <fstream>
#include <vector>
#include <ios>
#include <stdexcept>
void concat(const std::string& destination, const std::vector<std::string>& sources) {
// open the destination file and keep it open until all is done
std::ofstream dest_fs(destination, std::ios_base::binary);
if(!dest_fs)
throw std::runtime_error("Could not write to \"" + destination + "\".");
// loop over the source files
for(const auto& source_file : sources) {
// open the current source file
std::ifstream source_fs(source_file, std::ios_base::binary);
if(!source_fs)
throw std::runtime_error("Could not read from \"" + source_file + "\".");
// copy from source to destination
std::copy(std::istreambuf_iterator<char>(source_fs),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(dest_fs));
}
}
int cppmain(std::string program, std::vector<std::string> args) {
if(args.size() < 2) {
std::cout << "USAGE: " << program << " destination_file input_file(s)\n";
return 1;
}
// extract the first argument which is the destination file
std::string destination_file = std::move(args.front());
args.erase(args.begin()); // erase first argument from the vector
try {
// do the concatenation
concat(destination_file, args);
return 0;
} catch(const std::exception& ex) {
std::cerr << program << ": ERROR: " << ex.what() << "\n";
return 1;
}
}
int main(int argc, char* argv[]) {
return cppmain(argv[0], {argv + 1, argv + argc});
}
I've got C++ program that is getting data buffer from time to time, and should add it to existing compressed file.
I tried to make POC by reading 1k chunks from some file, passing them to compressed stream and uncompress it when the data is over.
I use Poco::DeflatingOutputStream to compress each chunk to the file, and Poco::InflatingOutputStream to check that after decompressing I get the original file.
However, it seems that after decompressing the stream my data went almost identical to the original file, except that between every 2 consecutive chunks of data i get a few garbage characters such as : à¿_ÿ
here's an example of line that is split between 2 chunks. the original line looks like that :
elevated=0 path=/System/Library/CoreServices/Dock.app/Contents/MacOS/Dock exist
while the decompressed line is :
elevated=0 path=/System/Libr à¿_ÿary/CoreServices/Dock.app/Contents/MacOS/Dock exist
May 19 19:12:51 PANMMUZNG8WNREM kernel[0]: pid=904 uid=1873876126 sbit=0
any idea what am i doing wrong. Here's my POC code:
int zip_unzip() {
std::ostringstream stream1;
Poco::DeflatingOutputStream gzipper(stream1, Poco::DeflatingStreamBuf::STREAM_ZLIB);
std::ifstream bigFile("/tmp/in.log");
constexpr size_t bufferSize = 1024;
char buffer[bufferSize];
while (bigFile) {
bigFile.read(buffer, bufferSize);
gzipper << buffer;
}
gzipper.close();
std::string zipped_string = stream1.str();
//////////////////
std::ofstream stream2("/tmp/out.log", std::ios::binary);
Poco::InflatingOutputStream gunzipper(stream2, InflatingStreamBuf::STREAM_ZLIB);
gunzipper << zipped_string;
gunzipper.close();
return 0;
}
Ok, i just realized i used the '<<' operator on each read from the HugeFile (the original decompressed file) without care, since there was no null termination symbol '/0' at the end of each window i read from the file.
That's the fixed version :
#include <stdio.h>
#include <fstream>
#include <Poco/DeflatingStream.h>
#include <Poco/Exception.h>
#include <iostream>
int BetterZip()
{
try {
// Create gzip file.
std::ofstream output_file("/tmp/out.gz", std::ios::binary);
Poco::DeflatingOutputStream output_stream(output_file, Poco::DeflatingStreamBuf::STREAM_GZIP);
// INPUT
std::ifstream big_file("/tmp/hugeFile");
constexpr size_t ReadBufferSize = 1024;
char buffer[ReadBufferSize];
while (big_file) {
big_file.read(buffer, ReadBufferSize);
output_stream.write(buffer, big_file.gcount());
}
output_stream.close();
} catch (const Poco::Exception& ex) {
std::cout << "Error : (error code " << ex.code() << " (" << ex.displayText() << ")";
return EINVAL;
}
return 0;
}
I want to input some contents to a file, but I'd like to check first if a file with the name I wish to create exists. If so, I don't want to create any file, even if the file is empty.
My attempt
bool CreateFile(char name[], char content[]){
std::ofstream file(name);
if(file){
std::cout << "This account already exists" << std::endl;
return false;
}
file << content;
file.close();
return true;
}
Is there any way to do what I want?
Assuming it is OK that the operation is not atomic, you can do:
if (std::ifstream(name))
{
std::cout << "File already exists" << std::endl;
return false;
}
std::ofstream file(name);
if (!file)
{
std::cout << "File could not be created" << std::endl;
return false;
}
...
Note that this doesn't work if you run multiple threads trying to create the same file, and certainly will not prevent a second process from "interfering" with the file creation because you have TOCTUI problems. [We first check if the file exists, and then create it - but someone else could have created it in between the check and the creation - if that's critical, you will need to do something else, which isn't portable].
A further problem is if you have permissions such as the file is not readable (so we can't open it for read) but is writeable, it will overwrite the file.
In MOST cases, neither of these things matter, because all you care about is telling someone that "you already have a file like that" (or something like that) in a "best effort" approach.
you can also use Boost.
boost::filesystem::exists( filename );
it works for files and folders.
And you will have an implementation close to something ready for C++14 in which filesystem should be part of the STL (see here).
Try
ifstream my_file("test.txt");
if (my_file)
{
// do stuff
}
From: How to check if a file exists and is readable in C++?
or you could use boost functions.
Try this (copied-ish from Erik Garrison: https://stackoverflow.com/a/3071528/575530)
#include <sys/stat.h>
bool FileExists(char* filename)
{
struct stat fileInfo;
return stat(filename, &fileInfo) == 0;
}
stat returns 0 if the file exists and -1 if not.
As of C++17 there is:
if (std::filesystem::exists(pathname)) {
...
Looked around a bit, and the only thing I find is using the open system call. It is the only function I found that allows you to create a file in a way that will fail if it already exists
#include <fcntl.h>
#include <errno.h>
int fd=open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
/* file exists or otherwise uncreatable
you might want to check errno*/
}else {
/* File is open to writing */
}
Note that you have to give permissions since you are creating a file.
This also removes any race conditions there might be
I just saw this test:
bool getFileExists(const TCHAR *file)
{
return (GetFileAttributes(file) != 0xFFFFFFFF);
}
C++17, cross-platform: Using std::filesystem::exists and std::filesystem::is_regular_file.
#include <filesystem> // C++17
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
bool CreateFile(const fs::path& filePath, const std::string& content)
{
try
{
if (fs::exists(filePath))
{
std::cout << filePath << " already exists.";
return false;
}
if (!fs::is_regular_file(filePath))
{
std::cout << filePath << " is not a regular file.";
return false;
}
}
catch (std::exception& e)
{
std::cerr << __func__ << ": An error occurred: " << e.what();
return false;
}
std::ofstream file(filePath);
file << content;
return true;
}
int main()
{
if (CreateFile("path/to/the/file.ext", "Content of the file"))
{
// Your business logic.
}
}
The easiest way to do this is using ios :: noreplace.
I am implementing file saving functionality within a Qt application using C++.
I am looking for a way to check to see if the selected file already exists before writing to it, so that I can prompt a warning to the user.
I am using an std::ofstream and I am not looking for a Boost solution.
This is one of my favorite tuck-away functions I keep on hand for multiple uses.
#include <sys/stat.h>
// Function: fileExists
/**
* Check if a file exists
*
* #param[in] filename - the name of the file to check
*
* #return true if the file exists, else false
*/
bool fileExists(const std::string& filename)
{
struct stat buf;
if (stat(filename.c_str(), &buf) != -1)
{
return true;
}
return false;
}
I find this much more tasteful than trying to open a file if you have no immediate intentions of using it for I/O.
bool fileExists(const char *fileName)
{
ifstream infile(fileName);
return infile.good();
}
This method is so far the shortest and most portable one. If the usage is not very sophisticated, this is one I would go for. If you also want to prompt a warning, I would do that in the main.
fstream file;
file.open("my_file.txt", ios_base::out | ios_base::in); // will not create file
if (file.is_open())
{
cout << "Warning, file already exists, proceed?";
if (no)
{
file.close();
// throw something
}
}
else
{
file.clear();
file.open("my_file.txt", ios_base::out); // will create if necessary
}
// do stuff with file
Note that in case of an existing file, this will open it in random-access mode. If you prefer, you can close it and reopen it in append mode or truncate mode.
With std::filesystem::exists of C++17:
#include <filesystem> // C++17
#include <iostream>
namespace fs = std::filesystem;
int main()
{
fs::path filePath("path/to/my/file.ext");
std::error_code ec; // For using the noexcept overload.
if (!fs::exists(filePath, ec) && !ec)
{
// Save to file, e.g. with std::ofstream file(filePath);
}
else
{
if (ec)
{
std::cerr << ec.message(); // Replace with your error handling.
}
else
{
std::cout << "File " << filePath << " does already exist.";
// Handle overwrite case.
}
}
}
See also std::error_code.
In case you want to check if the path you are writing to is actually a regular file, use std::filesystem::is_regular_file.
Try ::stat() (declared in <sys/stat.h>)
One of the way would be to do stat() and check on errno.
A sample code would look look this:
#include <sys/stat.h>
using namespace std;
// some lines of code...
int fileExist(const string &filePath) {
struct stat statBuff;
if (stat(filePath.c_str(), &statBuff) < 0) {
if (errno == ENOENT) return -ENOENT;
}
else
// do stuff with file
}
This works irrespective of the stream. If you still prefer to check using ofstream just check using is_open().
Example:
ofstream fp.open("<path-to-file>", ofstream::out);
if (!fp.is_open())
return false;
else
// do stuff with file
Hope this helps.
Thanks!