C++ ofstream create and write file issue - c++

i had problem with my virtual file system extractor.
ofstream ofs(path, ios::out|ios::binary);
ofs.write(file, length);
ofs.close();
Path is for a example "data/char/actormotion.txt", and there should be created file in directory data/char/ file named actormotion.txt but there is done nothing.

Check that it is open first
std::ofstream ofs(path, ios::out|ios::binary);
if (ofs.is_open())
{
// write stuff
ofs.close();
}
else
{
std::cout << "Error opening file";
}

More likely file wasn't opened. You may check this with bool is_open() method.
After opening stream is advisable check its state to make sure everything going well:
ofstream ofs(path, ios::out|ios::binary);
if ( (ofs.rdstate() & std::ofstream::failbit ) != 0 ){
//stream opened successfully, do the stuff...
in Windows system you may call GetLastError() right after to query error code.

Related

How can I prevent opening a QFile multiple time in parallel

I have written a function to check whether a file on disk is already in use. This is to avoid trying to execute a freshly downloaded installer while the antivirus is checking it, which fails.
The (generic) function looks like that:
bool isFileInUse(const QString& filePath)
{
QFile f(filePath);
if (!f.exists())
{
qDebug() << "File does not exist:" << filePath;
return false;
}
if (!f.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::ExistingOnly))
{
qDebug() << "File in use:" << filePath;
return true;
}
else
{
f.close();
qDebug() << "File free:" << filePath;
return false;
}
}
This works, I have tested manually with an installer (.exe) and it returns the expected result.
But now I want to write a unit test to check that function.
I have tried to create a file, and open it with QFile::open(QIODevice::WriteOnly), then call isFileInUse(..) on it, expecting to be already "in use", but it always returns false, i.e. Qt seem to have no problem to open twice the same file even in WriteOnly !
TEST(FilesUtils, isFileInUse)
{
QTemporaryDir dir;
const QString filePath = dir.filePath("test.txt");
createFile(filePath); // open, write dummy data and close the file
EXPECT_FALSE(FilesUtils::isFileInUse(filePath));
QFile f(filePath);
EXPECT_TRUE(f.open(QIODevice::WriteOnly | QIODevice::Append)); // OK
EXPECT_TRUE(FilesUtils::isFileInUse(filePath)); // FAILS, returns false
}
I have tried to open the file with a software like notepad.exe, and it also returns false.
Then I tried with Microsoft Word, and there it returns finally true (= file in use). But this is not portable and I cant expect a user to have Word installed on Windows, obviously.
Is there any way to open a file with Qt such that another QFile::open() returns false ? (i.e. lock the file)
Or does anyone sees something wrong in the code above ?
On Windows a file is opened for reading and/or writing and a share mode allowing additional reading/writing/deleting. This can create many interesting combinations, for example a file can be open for writing and allow additional open for reading but not writing. Or a file can be open for reading and allowing renames/deletes. See dwShareMode parameter of CreateFile WinAPI.
Unfortunately QFile::open API doesn't support share mode, since Qt is a portable framework and share mode exists only on Windows.
See these links with additional information for possible alternative solutions:
QLockFile
Qt: How to lock/prevent a file from being read while it is written?
Is possible to set QFile share permission?
Locking files using CreateFile on Windows
Once your target file has been created and opened, you should use the open() method with the QIODevice::NewOnly flag if it is to be called again.
QIODevice::NewOnly | 0x0040 | Fail if the file to be opened already exists. Create and open the file only if it does not exist. There is a guarantee from the operating system that you are the only one creating and opening the file. Note that this mode implies WriteOnly, and combining it with ReadWrite is allowed. This flag currently only affects QFile. Other classes might use this flag in the future, but until then using this flag with any classes other than QFile may result in undefined behavior. (since Qt 5.11)
Alternatively you could use QFile::isOpen() to test for prior file opening in function IsFileInUse:
if (f.isOpen()) return true;
Below is the code that proves the point (adapted from OP, which does not run out of the box):
#include <QString>
#include <QFile>
#include <QTemporaryDir>
#include <QtDebug>
#include <iostream>
void createFile(const QString& filePath)
{
// open, write dummy data and close the file
QFile f(filePath);
f.open(QIODevice::WriteOnly | QIODevice::Append);
f.write("dummy");
f.close();
}
#define EXPECT_FALSE(X) std::cerr << (X == false ? "OK, FALSE" : "NOT FALSE!") << std::endl;
#define EXPECT_TRUE(X) std::cerr << (X == true ? "OK, TRUE" : "NOT TRUE!") << std::endl;
class FilesUtils {
public:
static bool isFileInUse(const QString& filePath)
{
QFile f(filePath);
if (!f.exists())
{
qDebug() << "File does not exist:" << filePath;
return false;
}
if (!f.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::ExistingOnly))
{
qDebug() << "File in use:" << filePath;
return true;
}
else
{
f.close();
qDebug() << "File free:" << filePath;
return false;
}
}
};
int main()
{
QTemporaryDir dir;
const QString filePath = dir.filePath("test.txt");
if (QFileInfo(filePath).exists()) QFile(filePath).remove();
createFile(filePath); // open, write dummy data and close the file
EXPECT_FALSE(FilesUtils::isFileInUse(filePath));
QFile f(filePath);
EXPECT_TRUE(f.open(QIODevice::WriteOnly | QIODevice::Append)); // OK, returns true
EXPECT_TRUE(FilesUtils::isFileInUse(filePath)); // FAILS, returns false
f.close();
EXPECT_FALSE(f.open(QIODevice::WriteOnly | QIODevice::Append| QIODevice::NewOnly)); //OK, returns false
EXPECT_FALSE(FilesUtils::isFileInUse(filePath)); // SUCCEEDS, returns false
}
This code runs as expected:
File free: "/tmp/qt_temp-zCTbRf/test.txt"
OK, FALSE
OK, TRUE
File free: "/tmp/qt_temp-zCTbRf/test.txt"
NOT TRUE!
OK, FALSE
File free: "/tmp/qt_temp-zCTbRf/test.txt"
OK, FALSE

Infinite cycle due to QTextStream

So, I get infinite cycle while trying to read lines from file (line by line). I was trying to use do{}while(); cycle like that:
QTextStream stream(stdin);
QString line;
do {
line = stream.readLine();
} while (!line.isNull());
but I get empty string.
Sure, I checked file path (it is right). I was trying to use /Users/user/tts.txt path but without changes. I was trying to read other files (like m3u). And it's not working on macOS Catalina, Windows 10, Linux (Debian).
So, why did I get infinite cycle?
QStringList Manager::GetLinesFromFile(const QString &nameOfFile)
{
QStringList lines = {};
//path to file
const QString path = QCoreApplication::applicationDirPath() + "/bin/" + "tts.txt";
//"/Users/user/tts.txt"
QFile buffer;
buffer.QFile::setFileName(path);
#ifndef Q_DEBUG
qDebug() << path;
#endif
if(buffer.QFile::exists())
{
if(!buffer.QIODevice::open(QIODevice::ReadOnly))
{
#ifndef Q_DEBUG
qCritical() << "error: can't open file";
#endif
}
else
{
QTextStream stream(&buffer);
// both conditions
// (!stream.QTextStream::atEnd())
while(!buffer.QFileDevice::atEnd())
lines.QList::push_back(stream.QTextStream::readLine());
buffer.QFile::close();
}
}
else
{
#ifndef Q_DEBUG
qCritical() << "error: file not exists";
#endif
}
return lines;
}
Have a look at the QTextstream documentation https://doc.qt.io/qt-5/qtextstream.html. There is an example of reading line by line. Your while loop should read until the stream reaches the end of the buffer and many of the in built read functions will return false when his happens
So, I got it. I opened the file incorrectly.
I was using:
if(!file.QIODevice::open(QIODevice::ReadOnly))
but it should be like that:
if(!file.QFile::open(QFile::ReadOnly))

Is it necessary to flush a QTextStream before closing a QFile?

I need to log some text messages to a file with following requirements :
Each text messages is written in a new line at the end of the file.
Be reasonably sure that each message was correctly written to the file.
So far, the function is using QTextStream and QFile:
bool FileManager::appendLine(const QString &line)
{
if(!m_file.open(QIODevice::Append | QIODevice::Text)) // m_file is a QFile
return false;
QTextStream ts(&m_file);
ts << line << endl;
bool status = (ts.status() == QTextStream::Ok);
m_file.close();
return status;
}
Point 1 is satisfied but i have doubts about Point 2.
Even Qt Doc says that it is sufficient to close() the QFile to flush all its internal buffers :
void QFileDevice::close()
Reimplemented from QIODevice::close().
Calls QFileDevice::flush() and closes the file. Errors from flush are ignored.
What about the internal buffer of the QTextStream ?
Is it necessary to call QTextStream::flush() before closing the file ?
About Point 2, i guess that reading back the line just after it has been written would be the only way to be 100% sure of that. (for example a power failure may occur while the kernel has still datas in its buffers )
Thanks.
In your case, its not, because you are appending &endl in each write!
Writting &endl to the QTextStream writes '\n' to the stream and flushes the stream. It is Equivalent to: stream << '\n' << flush;
Further, when QTextStream is flushed due to &endl, it will empty all data from its write buffer into the device and call flush() on the device.
While this particular code will work because operations with QTextStream end with an endl, it's still better to ensure that QTextStream is completely and utterly finished working with the file when you close it. Just use scopes.
bool FileManager::appendLine(const QString &line)
{
if(!m_file.open(QIODevice::Append | QIODevice::Text)) // m_file is a QFile
return false;
bool status {false};
{
QTextStream ts(&m_file);
ts << line << endl;
status = (ts.status() == QTextStream::Ok);
}
m_file.close();
return status;
}

A way to know if the file currently is open before open it again?

I have a program which opens the same file several times.
I want to check before open any file if this file currently is open or not because I don't want to open the same file several times.
Is there a built-in function which can check if the file is currently open or any other way can do that?
The Code:
QString openFilePath = QFileDialog::getOpenFileName(this->mainWindow, "Open File");
if(openFilePath == ""){
return;
}
QFile openFile(openFilePath);
if(!openFile.open(QFile::ReadWrite)){
QMessageBox::critical(this->mainWindow, "Can't Open file", "Can't access to the file.");
}
QTextStream fileContent(&openFile);
QFileInfo fileInfo(openFile);
this->createEmptyFile(fileInfo.fileName());
this->txtEditor->setText(fileContent.readAll());
It seems to me that your question has really nothing to do with file opening in the programmatic sense, but is exclusively related to your application logic. You need to internally keep a list of all currently open files (in the sense that your GUI is showing such file), and do a check if the user opens a new file.
existing question
also you can try the hack) I dont know would it works or not, but: in the
void QFile::setFileName(const QString &name)
function overview QFile you can see that
Do not call this function if the file has already been opened.
Hmm) what if try to rename it in try catch to avoid crashing and if the renaming done, rename it again and open?) so you can try.
Regular file open, say for append, depending on the function, will usually return NULL or raise an exception if the file is already open. There are stateless file systems where this approach may not work.
std::fstream fs;
try {
fs.open("lk.txt", std::fstream::in | std::fstream::out | std::fstream::app);
fs << "We're way beyond the boundaries of the Pride Lands.";
} catch (const std::ios_base::failure &ex) {
// Something happened
std::cerr << ex.what() << std::endl;
}
fs.close();
In some file systems there are also shared open modes, which will explicitly let you concurrently reopen the file and do what you want with no errors generated.
here is the solution to your problem:-
is_open()
Scope:
std::ofstream::is_open
1.it Check if file is open or not?
2.it returns whether the stream is currently associated to a file
3.it is public member function of fstream.
4.parameters -none
Sample Code:
#include <iostream>
#include <fstream> // std::ofstream
int main ()
{
std::ofstream ofs;
ofs.open ("hye.txt");
if (ofs.is_open())
{
ofs << "hellow world";
cout << "successfully written to file";
ofs.close();
}
else
{
cout << "Error opening file";
}
return 0;
}
output:
successfully written to file

How to save poco uploaded file

I want to upload file using poco library.
Now I have the uploaded file in the istream variable but I don't know how I can save it to a file?
Here is my code where i can get the length of the file.
void handlePart(const MessageHeader &header, std::istream &stream) {
_type = header.get("Content-Type", "(unspecified)");
if (header.has("Content-Disposition")) {
std::string disp;
NameValueCollection params;
MessageHeader::splitParameters(header["Content-Disposition"], disp, params);
_name = params.get("name", "(unnamed)");
_fileName = params.get("filename", "(unnamed)");
}
CountingInputStream istr(stream);
NullOutputStream ostr;
StreamCopier::copyStream(istr, ostr);
_length = istr.chars();
}
Also now it's 1 file uploaded in the form if there be more than 1 file how it will be managed in istream?
Now there is 2 days I'm searching and testing different ways but I couldn't find any way, please help to resolve this problem.
Thank you in advanced.
Depend on #Abhinav question on this post:
We can write save code like this:
if (fileName.length() != 0){
std::ofstream fout( fileName, std::ios::binary);
fileList.push_back(fileName);
fout << stream.rdbuf() ;
fout.close();
}
But unfortunately it's working for one file if there is more than one this code can't catch correctly.